From df3167787f74f32caf4cf996986427ac7b6af70b Mon Sep 17 00:00:00 2001 From: korenkonder Date: Sun, 12 Feb 2023 04:13:59 +0300 Subject: [PATCH] Separation and small optimization. v0.7.1.0 --- extern/src/imgui/imconfig.h | 5 - extern/src/imgui/imgui.cpp | 3027 ++- extern/src/imgui/imgui.h | 478 +- extern/src/imgui/imgui_draw.cpp | 122 +- extern/src/imgui/imgui_impl_opengl3.cpp | 130 +- extern/src/imgui/imgui_impl_opengl3.h | 4 +- extern/src/imgui/imgui_impl_opengl3_loader.h | 8 + extern/src/imgui/imgui_impl_vulkan.cpp | 1450 ++ extern/src/imgui/imgui_impl_vulkan.h | 157 + extern/src/imgui/imgui_internal.h | 593 +- extern/src/imgui/imgui_tables.cpp | 84 +- extern/src/imgui/imgui_widgets.cpp | 628 +- extern/src/imgui/imstb_textedit.h | 36 +- extern/src/imgui/imstb_truetype.h | 2 +- extern/src/vk_mem_alloc.h | 19612 ++++++++++++++++ src/CRE/GL/uniform_buffer.hpp | 136 + src/CRE/Glitter/curve.cpp | 115 +- src/CRE/Glitter/effect_group.cpp | 16 +- src/CRE/Glitter/effect_inst.cpp | 20 +- src/CRE/Glitter/glitter.hpp | 41 +- src/CRE/Glitter/particle_manager.cpp | 61 +- src/CRE/Glitter/render_group.cpp | 32 +- src/CRE/Glitter/render_scene.cpp | 249 +- src/CRE/Glitter/scene.cpp | 4 +- src/CRE/Vulkan/buffer.cpp | 75 + src/CRE/Vulkan/buffer.hpp | 35 + src/CRE/Vulkan/command_buffer.cpp | 47 + src/CRE/Vulkan/command_buffer.hpp | 27 + src/CRE/Vulkan/fence.cpp | 51 + src/CRE/Vulkan/fence.hpp | 27 + src/CRE/Vulkan/framebuffer.cpp | 51 + src/CRE/Vulkan/framebuffer.hpp | 27 + src/CRE/Vulkan/image.cpp | 79 + src/CRE/Vulkan/image.hpp | 33 + src/CRE/Vulkan/image_view.cpp | 75 + src/CRE/Vulkan/image_view.hpp | 30 + src/CRE/Vulkan/semaphore.cpp | 43 + src/CRE/Vulkan/semaphore.hpp | 25 + src/CRE/auth_3d.cpp | 348 +- src/CRE/auth_3d.hpp | 60 +- src/CRE/camera.cpp | 5 + src/CRE/camera.hpp | 1 + src/CRE/data.cpp | 38 +- src/CRE/draw_object.cpp | 895 +- src/CRE/draw_object.hpp | 39 +- src/CRE/draw_pass.cpp | 1436 -- src/CRE/draw_pass.hpp | 13 - src/CRE/draw_task.cpp | 1021 - src/CRE/draw_task.hpp | 48 - src/CRE/gl_state.cpp | 84 +- src/CRE/gl_state.hpp | 16 +- src/CRE/hand_item.cpp | 223 + src/CRE/hand_item.hpp | 36 + src/CRE/item_table.cpp | 336 +- src/CRE/item_table.hpp | 20 +- src/CRE/light_param/face.cpp | 16 +- src/CRE/light_param/face.hpp | 16 +- src/CRE/light_param/fog.cpp | 49 +- src/CRE/light_param/fog.hpp | 16 +- src/CRE/light_param/light.cpp | 343 +- src/CRE/light_param/light.hpp | 48 +- src/CRE/light_param/wind.cpp | 12 +- src/CRE/light_param/wind.hpp | 12 +- src/CRE/mdata_manager.cpp | 12 +- src/CRE/mdata_manager.hpp | 5 +- src/CRE/object.cpp | 83 +- src/CRE/object.hpp | 8 +- src/CRE/ogg_vorbis.hpp | 2 +- src/CRE/post_process.cpp | 278 +- src/CRE/post_process.hpp | 18 +- src/CRE/post_process/aa.cpp | 38 +- src/CRE/post_process/aa.hpp | 3 +- src/CRE/post_process/blur.cpp | 91 +- src/CRE/post_process/blur.hpp | 2 + src/CRE/post_process/dof.cpp | 804 +- src/CRE/post_process/dof.hpp | 9 +- src/CRE/post_process/exposure.cpp | 133 +- src/CRE/post_process/exposure.hpp | 6 +- src/CRE/post_process/tone_map.cpp | 139 +- src/CRE/post_process/tone_map.hpp | 19 +- src/CRE/pv_db.cpp | 26 +- src/CRE/pv_db.hpp | 4 +- src/CRE/pv_expression.cpp | 6 +- src/CRE/render_context.cpp | 2936 ++- src/CRE/render_context.hpp | 977 +- src/CRE/render_manager.cpp | 1545 ++ src/CRE/render_manager.hpp | 13 + src/CRE/render_texture.cpp | 258 +- src/CRE/render_texture.hpp | 20 +- src/CRE/rob/ex_block.cpp | 618 +- src/CRE/rob/motion.cpp | 176 + src/CRE/rob/motion.hpp | 19 + src/CRE/rob/rob.cpp | 3138 ++- src/CRE/rob/rob.hpp | 159 +- src/CRE/rob/skin_param.cpp | 1218 + src/CRE/rob/skin_param.hpp | 24 + src/CRE/shader.cpp | 2508 +- src/CRE/shader.hpp | 472 +- src/CRE/shader_ft.cpp | 2082 +- src/CRE/shader_ft.hpp | 67 +- src/CRE/shader_glsl.cpp | 562 - src/CRE/shader_glsl.hpp | 71 - src/CRE/sound.cpp | 2 +- src/CRE/sound.hpp | 2 +- src/CRE/stage.cpp | 309 +- src/CRE/stage.hpp | 27 - src/CRE/stage_modern.cpp | 256 +- src/CRE/stage_modern.hpp | 7 - src/CRE/static_var.cpp | 2 +- src/CRE/static_var.hpp | 154 +- src/CRE/task.cpp | 204 +- src/CRE/task.hpp | 51 +- src/CRE/task_effect.cpp | 387 +- src/CRE/task_effect.hpp | 1 - src/CRE/texture.cpp | 28 +- src/CRE/texture.hpp | 14 +- src/DivaGL/DivaGL.rc | 92 +- src/DivaGL/bone_data.cpp | 3672 --- src/DivaGL/bone_data.hpp | 15 +- src/DivaGL/inject.cpp | 31 +- src/DivaGL/shader_table.cpp | 2 +- src/KKdLib/a3da.cpp | 764 +- src/KKdLib/alloc_data.cpp | 84 - src/KKdLib/alloc_data.hpp | 62 - src/KKdLib/database/auth_3d.cpp | 127 +- src/KKdLib/database/auth_3d.hpp | 7 +- src/KKdLib/database/bone.cpp | 92 +- src/KKdLib/database/bone.hpp | 31 +- src/KKdLib/database/hand_item.cpp | 206 + src/KKdLib/database/hand_item.hpp | 46 + src/KKdLib/database/item_table.cpp | 293 +- src/KKdLib/database/motion.cpp | 132 +- src/KKdLib/database/motion.hpp | 24 +- src/KKdLib/database/object.cpp | 65 +- src/KKdLib/database/object.hpp | 78 +- src/KKdLib/database/stage.cpp | 16 +- src/KKdLib/database/stage.hpp | 10 +- src/KKdLib/database/texture.cpp | 54 +- src/KKdLib/database/texture.hpp | 5 +- src/KKdLib/dds.cpp | 154 +- src/KKdLib/farc.cpp | 26 +- src/KKdLib/hash.cpp | 28 +- src/KKdLib/image.cpp | 14 +- src/KKdLib/image.hpp | 6 +- src/KKdLib/key_val.cpp | 30 +- src/KKdLib/key_val.hpp | 2 +- src/KKdLib/mat.cpp | 123 +- src/KKdLib/mat.hpp | 5 + src/KKdLib/mot.cpp | 16 +- src/KKdLib/mot.hpp | 6 +- src/KKdLib/obj.cpp | 287 +- src/KKdLib/obj.hpp | 12 +- src/KKdLib/prj/algorithm.hpp | 40 + src/KKdLib/prj/shared_ptr.cpp | 6 - src/KKdLib/prj/stack_allocator.cpp | 100 + src/KKdLib/prj/stack_allocator.hpp | 85 + src/KKdLib/pv_exp.cpp | 16 +- src/KKdLib/pv_exp.hpp | 6 +- src/{CRE => KKdLib}/time.cpp | 0 src/{CRE => KKdLib}/time.hpp | 2 +- src/{CRE => KKdLib}/timer.cpp | 0 src/{CRE => KKdLib}/timer.hpp | 2 +- src/KKdLib/txp.cpp | 24 +- src/KKdLib/txp.hpp | 12 +- src/{CRE => KKdLib}/waitable_timer.hpp | 2 +- src/ReDIVA/classes/data_view/draw_task.cpp | 87 +- src/ReDIVA/classes/glitter_editor.cpp | 61 +- src/ReDIVA/classes/graphics/light.cpp | 2 +- src/ReDIVA/classes/graphics/post_process.cpp | 2 + .../classes/graphics/render_settings.cpp | 2 +- src/ReDIVA/data_edit.cpp | 6 +- src/ReDIVA/data_initialize.cpp | 18 +- src/ReDIVA/data_initialize.hpp | 4 +- src/ReDIVA/data_test/auth_3d_test.cpp | 285 +- src/ReDIVA/data_test/auth_3d_test.hpp | 63 + src/ReDIVA/data_test/glitter_test.cpp | 10 +- src/ReDIVA/data_test/motion_test.cpp | 783 + src/ReDIVA/data_test/motion_test.hpp | 198 + src/ReDIVA/data_test/stage_test.cpp | 324 +- src/ReDIVA/data_test/stage_test.hpp | 50 +- src/ReDIVA/dw.cpp | 386 + src/ReDIVA/dw.hpp | 252 + src/ReDIVA/game_state.cpp | 398 +- src/ReDIVA/game_state.hpp | 326 - src/ReDIVA/imgui_helper.cpp | 120 +- src/ReDIVA/imgui_helper.hpp | 6 +- src/ReDIVA/input.cpp | 6 +- src/ReDIVA/main.cpp | 410 +- src/ReDIVA/pv_game.cpp | 1673 +- src/ReDIVA/pv_game.hpp | 59 +- src/ReDIVA/render.cpp | 2452 +- src/ReDIVA/render.hpp | 5 +- src/ReDIVA/system_startup.cpp | 26 +- src/ReDIVA/system_startup.hpp | 4 +- src/ReDIVA/task_window.cpp | 4 +- src/ReDIVA/x_pv_game.cpp | 175 +- thirdparty.txt | 28 +- 197 files changed, 44235 insertions(+), 23661 deletions(-) create mode 100644 extern/src/imgui/imgui_impl_vulkan.cpp create mode 100644 extern/src/imgui/imgui_impl_vulkan.h create mode 100644 extern/src/vk_mem_alloc.h create mode 100644 src/CRE/GL/uniform_buffer.hpp create mode 100644 src/CRE/Vulkan/buffer.cpp create mode 100644 src/CRE/Vulkan/buffer.hpp create mode 100644 src/CRE/Vulkan/command_buffer.cpp create mode 100644 src/CRE/Vulkan/command_buffer.hpp create mode 100644 src/CRE/Vulkan/fence.cpp create mode 100644 src/CRE/Vulkan/fence.hpp create mode 100644 src/CRE/Vulkan/framebuffer.cpp create mode 100644 src/CRE/Vulkan/framebuffer.hpp create mode 100644 src/CRE/Vulkan/image.cpp create mode 100644 src/CRE/Vulkan/image.hpp create mode 100644 src/CRE/Vulkan/image_view.cpp create mode 100644 src/CRE/Vulkan/image_view.hpp create mode 100644 src/CRE/Vulkan/semaphore.cpp create mode 100644 src/CRE/Vulkan/semaphore.hpp delete mode 100644 src/CRE/draw_pass.cpp delete mode 100644 src/CRE/draw_pass.hpp delete mode 100644 src/CRE/draw_task.cpp delete mode 100644 src/CRE/draw_task.hpp create mode 100644 src/CRE/hand_item.cpp create mode 100644 src/CRE/hand_item.hpp create mode 100644 src/CRE/render_manager.cpp create mode 100644 src/CRE/render_manager.hpp create mode 100644 src/CRE/rob/motion.cpp create mode 100644 src/CRE/rob/motion.hpp create mode 100644 src/CRE/rob/skin_param.cpp create mode 100644 src/CRE/rob/skin_param.hpp delete mode 100644 src/CRE/shader_glsl.cpp delete mode 100644 src/CRE/shader_glsl.hpp delete mode 100644 src/KKdLib/alloc_data.cpp delete mode 100644 src/KKdLib/alloc_data.hpp create mode 100644 src/KKdLib/database/hand_item.cpp create mode 100644 src/KKdLib/database/hand_item.hpp create mode 100644 src/KKdLib/prj/algorithm.hpp delete mode 100644 src/KKdLib/prj/shared_ptr.cpp create mode 100644 src/KKdLib/prj/stack_allocator.cpp create mode 100644 src/KKdLib/prj/stack_allocator.hpp rename src/{CRE => KKdLib}/time.cpp (100%) rename src/{CRE => KKdLib}/time.hpp (87%) rename src/{CRE => KKdLib}/timer.cpp (100%) rename src/{CRE => KKdLib}/timer.hpp (95%) rename src/{CRE => KKdLib}/waitable_timer.hpp (97%) create mode 100644 src/ReDIVA/data_test/motion_test.cpp create mode 100644 src/ReDIVA/data_test/motion_test.hpp create mode 100644 src/ReDIVA/dw.cpp create mode 100644 src/ReDIVA/dw.hpp diff --git a/extern/src/imgui/imconfig.h b/extern/src/imgui/imconfig.h index e3dc27f6..ed265082 100644 --- a/extern/src/imgui/imconfig.h +++ b/extern/src/imgui/imconfig.h @@ -108,11 +108,6 @@ //#define IM_DEBUG_BREAK IM_ASSERT(0) //#define IM_DEBUG_BREAK __debugbreak() -//---- Debug Tools: Have the Item Picker break in the ItemAdd() function instead of ItemHoverable(), -// (which comes earlier in the code, will catch a few extra items, allow picking items other than Hovered one.) -// This adds a small runtime cost which is why it is not enabled by default. -//#define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX - //---- Debug Tools: Enable slower asserts //#define IMGUI_DEBUG_PARANOID diff --git a/extern/src/imgui/imgui.cpp b/extern/src/imgui/imgui.cpp index b979938d..3faca98f 100644 --- a/extern/src/imgui/imgui.cpp +++ b/extern/src/imgui/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.88 +// dear imgui, v1.89.2 // (main code and documentation) // Help: @@ -65,10 +65,11 @@ CODE // [SECTION] MISC HELPERS/UTILITIES (Color functions) // [SECTION] ImGuiStorage // [SECTION] ImGuiTextFilter -// [SECTION] ImGuiTextBuffer +// [SECTION] ImGuiTextBuffer, ImGuiTextIndex // [SECTION] ImGuiListClipper // [SECTION] STYLING // [SECTION] RENDER HELPERS +// [SECTION] INITIALIZATION, SHUTDOWN // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) // [SECTION] INPUTS // [SECTION] ERROR CHECKING @@ -80,7 +81,8 @@ CODE // [SECTION] DRAG AND DROP // [SECTION] LOGGING/CAPTURING // [SECTION] SETTINGS -// [SECTION] VIEWPORTS +// [SECTION] LOCALIZATION +// [SECTION] VIEWPORTS, PLATFORM WINDOWS // [SECTION] PLATFORM DEPENDENT HELPERS // [SECTION] METRICS/DEBUGGER WINDOW // [SECTION] DEBUG LOG WINDOW @@ -126,13 +128,13 @@ CODE - Hold SHIFT or use mouse to select text. - CTRL+Left/Right to word jump. - CTRL+Shift+Left/Right to select words. - - CTRL+A our Double-Click to select all. + - CTRL+A or Double-Click to select all. - CTRL+X,CTRL+C,CTRL+V to use OS clipboard/ - CTRL+Z,CTRL+Y to undo/redo. - ESCAPE to revert text to its original value. - Controls are automatically adjusted for OSX to match standard OSX text editing operations. - General Keyboard controls: enable with ImGuiConfigFlags_NavEnableKeyboard. - - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. See suggested mappings in imgui.h ImGuiNavInput_ + download PNG/PSD at http://dearimgui.org/controls_sheets + - General Gamepad controls: enable with ImGuiConfigFlags_NavEnableGamepad. Download controller mapping PNG/PSD at http://dearimgui.org/controls_sheets PROGRAMMER GUIDE @@ -290,7 +292,7 @@ CODE --------------------------------------------- The backends in impl_impl_XXX.cpp files contain many working implementations of a rendering function. - void void MyImGuiRenderFunction(ImDrawData* draw_data) + void MyImGuiRenderFunction(ImDrawData* draw_data) { // TODO: Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled // TODO: Setup texture sampling state: sample with bilinear filtering (NOT point/nearest filtering). Use 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines;' to allow point/nearest filtering. @@ -345,12 +347,10 @@ CODE USING GAMEPAD/KEYBOARD NAVIGATION CONTROLS ------------------------------------------ - The gamepad/keyboard navigation is fairly functional and keeps being improved. - - Gamepad support is particularly useful to use Dear ImGui on a console system (e.g. PS4, Switch, XB1) without a mouse! - - You can ask questions and report issues at https://github.com/ocornut/imgui/issues/787 + - Gamepad support is particularly useful to use Dear ImGui on a console system (e.g. PlayStation, Switch, Xbox) without a mouse! - The initial focus was to support game controllers, but keyboard is becoming increasingly and decently usable. - Keyboard: - Application: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. - - Internally: NewFrame() will automatically fill io.NavInputs[] based on backend's io.AddKeyEvent() calls. - When keyboard navigation is active (io.NavActive + ImGuiConfigFlags_NavEnableKeyboard), the io.WantCaptureKeyboard flag will be set. For more advanced uses, you may want to read from: - io.NavActive: true when a window is focused and it doesn't have the ImGuiWindowFlags_NoNavInputs flag set. @@ -362,8 +362,7 @@ CODE - Backend: Set io.BackendFlags |= ImGuiBackendFlags_HasGamepad + call io.AddKeyEvent/AddKeyAnalogEvent() with ImGuiKey_Gamepad_XXX keys. For analog values (0.0f to 1.0f), backend is responsible to handling a dead-zone and rescaling inputs accordingly. Backend code will probably need to transform your raw inputs (such as e.g. remapping your 0.2..0.9 raw input range to 0.0..1.0 imgui range, etc.). - - Internally: NewFrame() will automatically fill io.NavInputs[] based on backend's io.AddKeyEvent() + io.AddKeyAnalogEvent() calls. - - BEFORE 1.87, BACKENDS USED TO WRITE DIRECTLY TO io.NavInputs[]. This is going to be obsoleted in the future. Please call io functions instead! + - BEFORE 1.87, BACKENDS USED TO WRITE TO io.NavInputs[]. This is now obsolete. Please call io functions instead! - You can download PNG/PSD files depicting the gamepad controls for common controllers at: http://dearimgui.org/controls_sheets - If you need to share inputs between your game and the Dear ImGui interface, the easiest approach is to go all-or-nothing, with a buttons combo to toggle the target. Please reach out if you think the game vs navigation input sharing could be improved. @@ -387,6 +386,51 @@ CODE When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2022/10/26 (1.89) - commented out redirecting OpenPopupContextItem() which was briefly the name of OpenPopupOnItemClick() from 1.77 to 1.79. + - 2022/10/12 (1.89) - removed runtime patching of invalid "%f"/"%0.f" format strings for DragInt()/SliderInt(). This was obsoleted in 1.61 (May 2018). See 1.61 changelog for details. + - 2022/09/26 (1.89) - renamed and merged keyboard modifiers key enums and flags into a same set. Kept inline redirection enums (will obsolete). + - ImGuiKey_ModCtrl and ImGuiModFlags_Ctrl -> ImGuiMod_Ctrl + - ImGuiKey_ModShift and ImGuiModFlags_Shift -> ImGuiMod_Shift + - ImGuiKey_ModAlt and ImGuiModFlags_Alt -> ImGuiMod_Alt + - ImGuiKey_ModSuper and ImGuiModFlags_Super -> ImGuiMod_Super + the ImGuiKey_ModXXX were introduced in 1.87 and mostly used by backends. + the ImGuiModFlags_XXX have been exposed in imgui.h but not really used by any public api only by third-party extensions. + exceptionally commenting out the older ImGuiKeyModFlags_XXX names ahead of obsolescence schedule to reduce confusion and because they were not meant to be used anyway. + - 2022/09/20 (1.89) - ImGuiKey is now a typed enum, allowing ImGuiKey_XXX symbols to be named in debuggers. + this will require uses of legacy backend-dependent indices to be casted, e.g. + - with imgui_impl_glfw: IsKeyPressed(GLFW_KEY_A) -> IsKeyPressed((ImGuiKey)GLFW_KEY_A); + - with imgui_impl_win32: IsKeyPressed('A') -> IsKeyPressed((ImGuiKey)'A') + - etc. However if you are upgrading code you might well use the better, backend-agnostic IsKeyPressed(ImGuiKey_A) now! + - 2022/09/12 (1.89) - removed the bizarre legacy default argument for 'TreePush(const void* ptr = NULL)', always pass a pointer value explicitly. NULL/nullptr is ok but require cast, e.g. TreePush((void*)nullptr); + - 2022/09/05 (1.89) - commented out redirecting functions/enums names that were marked obsolete in 1.77 and 1.78 (June 2020): + - DragScalar(), DragScalarN(), DragFloat(), DragFloat2(), DragFloat3(), DragFloat4(): For old signatures ending with (..., const char* format, float power = 1.0f) -> use (..., format ImGuiSliderFlags_Logarithmic) if power != 1.0f. + - SliderScalar(), SliderScalarN(), SliderFloat(), SliderFloat2(), SliderFloat3(), SliderFloat4(): For old signatures ending with (..., const char* format, float power = 1.0f) -> use (..., format ImGuiSliderFlags_Logarithmic) if power != 1.0f. + - BeginPopupContextWindow(const char*, ImGuiMouseButton, bool) -> use BeginPopupContextWindow(const char*, ImGuiPopupFlags) + - 2022/09/02 (1.89) - obsoleted using SetCursorPos()/SetCursorScreenPos() to extend parent window/cell boundaries. + this relates to when moving the cursor position beyond current boundaries WITHOUT submitting an item. + - previously this would make the window content size ~200x200: + Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End(); + - instead, please submit an item: + Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End(); + - alternative: + Begin(...) + Dummy(ImVec2(200,200)) + End(); + - content size is now only extended when submitting an item! + - with '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will now be detected and assert. + - without '#define IMGUI_DISABLE_OBSOLETE_FUNCTIONS' this will silently be fixed until we obsolete it. + - 2022/08/03 (1.89) - changed signature of ImageButton() function. Kept redirection function (will obsolete). + - added 'const char* str_id' parameter + removed 'int frame_padding = -1' parameter. + - old signature: bool ImageButton(ImTextureID tex_id, ImVec2 size, ImVec2 uv0 = ImVec2(0,0), ImVec2 uv1 = ImVec2(1,1), int frame_padding = -1, ImVec4 bg_col = ImVec4(0,0,0,0), ImVec4 tint_col = ImVec4(1,1,1,1)); + - used the ImTextureID value to create an ID. This was inconsistent with other functions, led to ID conflicts, and caused problems with engines using transient ImTextureID values. + - had a FramePadding override which was inconsistent with other functions and made the already-long signature even longer. + - new signature: bool ImageButton(const char* str_id, ImTextureID tex_id, ImVec2 size, ImVec2 uv0 = ImVec2(0,0), ImVec2 uv1 = ImVec2(1,1), ImVec4 bg_col = ImVec4(0,0,0,0), ImVec4 tint_col = ImVec4(1,1,1,1)); + - requires an explicit identifier. You may still use e.g. PushID() calls and then pass an empty identifier. + - always uses style.FramePadding for padding, to be consistent with other buttons. You may use PushStyleVar() to alter this. + - 2022/07/08 (1.89) - inputs: removed io.NavInputs[] and ImGuiNavInput enum (following 1.87 changes). + - Official backends from 1.87+ -> no issue. + - Official backends from 1.60 to 1.86 -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need updating! + - Custom backends not writing to io.NavInputs[] -> no issue. + - Custom backends writing to io.NavInputs[] -> will build and convert gamepad inputs, unless IMGUI_DISABLE_OBSOLETE_KEYIO is defined. Need fixing! + - TL;DR: Backends should call io.AddKeyEvent()/io.AddKeyAnalogEvent() with ImGuiKey_GamepadXXX values instead of filling io.NavInput[]. - 2022/06/15 (1.88) - renamed IMGUI_DISABLE_METRICS_WINDOW to IMGUI_DISABLE_DEBUG_TOOLS for correctness. kept support for old define (will obsolete). - 2022/05/03 (1.88) - backends: osx: removed ImGui_ImplOSX_HandleEvent() from backend API in favor of backend automatically handling event capture. All ImGui_ImplOSX_HandleEvent() calls should be removed as they are now unnecessary. - 2022/04/05 (1.88) - inputs: renamed ImGuiKeyModFlags to ImGuiModFlags. Kept inline redirection enums (will obsolete). This was never used in public API functions but technically present in imgui.h and ImGuiIO. @@ -404,7 +448,7 @@ CODE - IsKeyPressed(MY_NATIVE_KEY_XXX) -> use IsKeyPressed(ImGuiKey_XXX) - IsKeyPressed(GetKeyIndex(ImGuiKey_XXX)) -> use IsKeyPressed(ImGuiKey_XXX) - Backend writing to io.KeyMap[],io.KeysDown[] -> backend should call io.AddKeyEvent() (+ call io.SetKeyEventNativeData() if you want legacy user code to stil function with legacy key codes). - - Backend writing to io.KeyCtrl, io.KeyShift.. -> backend should call io.AddKeyEvent() with ImGuiKey_ModXXX values. *IF YOU PULLED CODE BETWEEN 2021/01/10 and 2021/01/27: We used to have a io.AddKeyModsEvent() function which was now replaced by io.AddKeyEvent() with ImGuiKey_ModXXX values.* + - Backend writing to io.KeyCtrl, io.KeyShift.. -> backend should call io.AddKeyEvent() with ImGuiMod_XXX values. *IF YOU PULLED CODE BETWEEN 2021/01/10 and 2021/01/27: We used to have a io.AddKeyModsEvent() function which was now replaced by io.AddKeyEvent() with ImGuiMod_XXX values.* - one case won't work with backward compatibility: if your custom backend used ImGuiKey as mock native indices (e.g. "io.KeyMap[ImGuiKey_A] = ImGuiKey_A") because those values are now larger than the legacy KeyDown[] array. Will assert. - inputs: added ImGuiKey_ModCtrl/ImGuiKey_ModShift/ImGuiKey_ModAlt/ImGuiKey_ModSuper values to submit keyboard modifiers using io.AddKeyEvent(), instead of writing directly to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper. - 2022/01/05 (1.87) - inputs: renamed ImGuiKey_KeyPadEnter to ImGuiKey_KeypadEnter to align with new symbols. Kept redirection enum. @@ -765,7 +809,7 @@ CODE - How can I have widgets with an empty label? - How can I have multiple widgets with the same label? - How can I have multiple windows with the same label? - Q: How can I display an image? What is ImTextureID, how does it works? + Q: How can I display an image? What is ImTextureID, how does it work? Q: How can I use my own math types instead of ImVec2/ImVec4? Q: How can I interact with standard C++ types (such as std::string and std::vector)? Q: How can I display custom shapes? (using low-level ImDrawList API) @@ -824,7 +868,6 @@ CODE #include "imgui_internal.h" // System includes -#include // toupper #include // vsnprintf, sscanf, printf #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier #include // intptr_t @@ -871,7 +914,7 @@ CODE #if defined(_MSC_VER) && _MSC_VER >= 1922 // MSVC 2019 16.2 or later #pragma warning (disable: 5054) // operator '|': deprecated between enumerations of different types #endif -#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). +#pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to an 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). #pragma warning (disable: 26495) // [Static Analyzer] Variable 'XXX' is uninitialized. Always initialize a member variable (type.6). #pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). #endif @@ -894,7 +937,7 @@ CODE #pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double. #pragma clang diagnostic ignored "-Wimplicit-int-float-conversion" // warning: implicit conversion from 'xxx' to 'float' may lose precision #elif defined(__GNUC__) -// We disable -Wpragmas because GCC doesn't provide an has_warning equivalent and some forks/patches may not following the warning/version association. +// We disable -Wpragmas because GCC doesn't provide a has_warning equivalent and some forks/patches may not follow the warning/version association. #pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used #pragma GCC diagnostic ignored "-Wint-to-pointer-cast" // warning: cast to pointer from integer of different size @@ -918,7 +961,7 @@ static const float NAV_WINDOWING_LIST_APPEAR_DELAY = 0.15f; // Time // Window resizing from edges (when io.ConfigWindowsResizeFromEdges = true and ImGuiBackendFlags_HasMouseCursors is set in io.BackendFlags by backend) static const float WINDOWS_HOVER_PADDING = 4.0f; // Extend outside window for hovering/resizing (maxxed with TouchPadding) and inside windows for borders. Affect FindHoveredWindow(). static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time. -static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 2.00f; // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved. +static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 0.70f; // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved. //------------------------------------------------------------------------- // [SECTION] FORWARD DECLARATIONS @@ -974,14 +1017,17 @@ static void ErrorCheckEndFrameSanityChecks(); static void UpdateDebugToolItemPicker(); static void UpdateDebugToolStackQueries(); -// Misc -static void UpdateSettings(); +// Inputs static void UpdateKeyboardInputs(); static void UpdateMouseInputs(); static void UpdateMouseWheel(); +static void UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt); + +// Misc +static void UpdateSettings(); static bool UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect); static void RenderWindowOuterBorders(ImGuiWindow* window); -static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size); +static void RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size); static void RenderWindowTitleBarContents(ImGuiWindow* window, const ImRect& title_bar_rect, const char* name, bool* p_open); static void RenderDimmedBackgroundBehindWindow(ImGuiWindow* window, ImU32 col); static void RenderDimmedBackgrounds(); @@ -1068,7 +1114,7 @@ ImGuiStyle::ImGuiStyle() LogSliderDeadzone = 4.0f; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero. TabRounding = 4.0f; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. TabBorderSize = 0.0f; // Thickness of border around tabs. - TabMinWidthForCloseButton = 0.0f; // Minimum width for close button to appears on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected. + TabMinWidthForCloseButton = 0.0f; // Minimum width for close button to appear on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected. ColorButtonPosition = ImGuiDir_Right; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. ButtonTextAlign = ImVec2(0.5f,0.5f);// Alignment of button text when button is larger than text. SelectableTextAlign = ImVec2(0.0f,0.0f);// Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. @@ -1136,6 +1182,8 @@ ImGuiIO::ImGuiIO() #endif KeyRepeatDelay = 0.275f; KeyRepeatRate = 0.050f; + HoverDelayNormal = 0.30f; + HoverDelayShort = 0.10f; UserData = NULL; Fonts = NULL; @@ -1153,6 +1201,8 @@ ImGuiIO::ImGuiIO() #endif ConfigInputTrickleEventQueue = true; ConfigInputTextCursorBlink = true; + ConfigInputTextEnterKeepActive = false; + ConfigDragClickToInputText = false; ConfigWindowsResizeFromEdges = true; ConfigWindowsMoveFromTitleBarOnly = false; ConfigMemoryCompactTimer = 60.0f; @@ -1171,7 +1221,6 @@ ImGuiIO::ImGuiIO() MouseDragThreshold = 6.0f; for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f; for (int i = 0; i < IM_ARRAYSIZE(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; } - for (int i = 0; i < IM_ARRAYSIZE(NavInputsDownDuration); i++) NavInputsDownDuration[i] = -1.0f; AppAcceptingEvents = true; BackendUsingLegacyKeyArrays = (ImS8)-1; BackendUsingLegacyNavInputArray = true; // assume using legacy array until proven wrong @@ -1244,11 +1293,13 @@ void ImGuiIO::AddInputCharactersUTF8(const char* utf8_chars) } } +// FIXME: Perhaps we could clear queued events as well? void ImGuiIO::ClearInputCharacters() { InputQueueCharacters.resize(0); } +// FIXME: Perhaps we could clear queued events as well? void ImGuiIO::ClearInputKeys() { #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO @@ -1261,9 +1312,31 @@ void ImGuiIO::ClearInputKeys() KeysData[n].DownDurationPrev = -1.0f; } KeyCtrl = KeyShift = KeyAlt = KeySuper = false; - KeyMods = ImGuiModFlags_None; - for (int n = 0; n < IM_ARRAYSIZE(NavInputsDownDuration); n++) - NavInputsDownDuration[n] = NavInputsDownDurationPrev[n] = -1.0f; + KeyMods = ImGuiMod_None; + MousePos = ImVec2(-FLT_MAX, -FLT_MAX); + for (int n = 0; n < IM_ARRAYSIZE(MouseDown); n++) + { + MouseDown[n] = false; + MouseDownDuration[n] = MouseDownDurationPrev[n] = -1.0f; + } + MouseWheel = MouseWheelH = 0.0f; +} + +static ImGuiInputEvent* FindLatestInputEvent(ImGuiInputEventType type, int arg = -1) +{ + ImGuiContext& g = *GImGui; + for (int n = g.InputEventsQueue.Size - 1; n >= 0; n--) + { + ImGuiInputEvent* e = &g.InputEventsQueue[n]; + if (e->Type != type) + continue; + if (type == ImGuiInputEventType_Key && e->Key.Key != arg) + continue; + if (type == ImGuiInputEventType_MouseButton && e->MouseButton.Button != arg) + continue; + return e; + } + return NULL; } // Queue a new key down/up event. @@ -1277,7 +1350,9 @@ void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value) return; ImGuiContext& g = *GImGui; IM_ASSERT(&g.IO == this && "Can only add events to current context."); - IM_ASSERT(ImGui::IsNamedKey(key)); // Backend needs to pass a valid ImGuiKey_ constant. 0..511 values are legacy native key codes which are not accepted by this API. + IM_ASSERT(ImGui::IsNamedKeyOrModKey(key)); // Backend needs to pass a valid ImGuiKey_ constant. 0..511 values are legacy native key codes which are not accepted by this API. + IM_ASSERT(!ImGui::IsAliasKey(key)); // Backend cannot submit ImGuiKey_MouseXXX values they are automatically inferred from AddMouseXXX() events. + IM_ASSERT(key != ImGuiMod_Shortcut); // We could easily support the translation here but it seems saner to not accept it (TestEngine perform a translation itself) // Verify that backend isn't mixing up using new io.AddKeyEvent() api and old io.KeysDown[] + io.KeyMap[] data. #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO @@ -1290,17 +1365,13 @@ void ImGuiIO::AddKeyAnalogEvent(ImGuiKey key, bool down, float analog_value) if (ImGui::IsGamepadKey(key)) BackendUsingLegacyNavInputArray = false; - // Partial filter of duplicates (not strictly needed, but makes data neater in particular for key mods and gamepad values which are most commonly spmamed) - ImGuiKeyData* key_data = ImGui::GetKeyData(key); - if (key_data->Down == down && key_data->AnalogValue == analog_value) - { - bool found = false; - for (int n = g.InputEventsQueue.Size - 1; n >= 0 && !found; n--) - if (g.InputEventsQueue[n].Type == ImGuiInputEventType_Key && g.InputEventsQueue[n].Key.Key == key) - found = true; - if (!found) - return; - } + // Filter duplicate (in particular: key mods and gamepad analog values are commonly spammed) + const ImGuiInputEvent* latest_event = FindLatestInputEvent(ImGuiInputEventType_Key, (int)key); + const ImGuiKeyData* key_data = ImGui::GetKeyData(key); + const bool latest_key_down = latest_event ? latest_event->Key.Down : key_data->Down; + const float latest_key_analog = latest_event ? latest_event->Key.AnalogValue : key_data->AnalogValue; + if (latest_key_down == down && latest_key_analog == analog_value) + return; // Add event ImGuiInputEvent e; @@ -1327,14 +1398,14 @@ void ImGuiIO::SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native if (key == ImGuiKey_None) return; IM_ASSERT(ImGui::IsNamedKey(key)); // >= 512 - IM_ASSERT(native_legacy_index == -1 || ImGui::IsLegacyKey(native_legacy_index)); // >= 0 && <= 511 + IM_ASSERT(native_legacy_index == -1 || ImGui::IsLegacyKey((ImGuiKey)native_legacy_index)); // >= 0 && <= 511 IM_UNUSED(native_keycode); // Yet unused IM_UNUSED(native_scancode); // Yet unused // Build native->imgui map so old user code can still call key functions with native 0..511 values. #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO const int legacy_key = (native_legacy_index != -1) ? native_legacy_index : native_keycode; - if (!ImGui::IsLegacyKey(legacy_key)) + if (!ImGui::IsLegacyKey((ImGuiKey)legacy_key)) return; KeyMap[legacy_key] = key; KeyMap[key] = legacy_key; @@ -1358,11 +1429,20 @@ void ImGuiIO::AddMousePosEvent(float x, float y) if (!AppAcceptingEvents) return; + // Apply same flooring as UpdateMouseInputs() + ImVec2 pos((x > -FLT_MAX) ? ImFloorSigned(x) : x, (y > -FLT_MAX) ? ImFloorSigned(y) : y); + + // Filter duplicate + const ImGuiInputEvent* latest_event = FindLatestInputEvent(ImGuiInputEventType_MousePos); + const ImVec2 latest_pos = latest_event ? ImVec2(latest_event->MousePos.PosX, latest_event->MousePos.PosY) : g.IO.MousePos; + if (latest_pos.x == pos.x && latest_pos.y == pos.y) + return; + ImGuiInputEvent e; e.Type = ImGuiInputEventType_MousePos; e.Source = ImGuiInputSource_Mouse; - e.MousePos.PosX = x; - e.MousePos.PosY = y; + e.MousePos.PosX = pos.x; + e.MousePos.PosY = pos.y; g.InputEventsQueue.push_back(e); } @@ -1374,6 +1454,12 @@ void ImGuiIO::AddMouseButtonEvent(int mouse_button, bool down) if (!AppAcceptingEvents) return; + // Filter duplicate + const ImGuiInputEvent* latest_event = FindLatestInputEvent(ImGuiInputEventType_MouseButton, (int)mouse_button); + const bool latest_button_down = latest_event ? latest_event->MouseButton.Down : g.IO.MouseDown[mouse_button]; + if (latest_button_down == down) + return; + ImGuiInputEvent e; e.Type = ImGuiInputEventType_MouseButton; e.Source = ImGuiInputSource_Mouse; @@ -1387,7 +1473,9 @@ void ImGuiIO::AddMouseWheelEvent(float wheel_x, float wheel_y) { ImGuiContext& g = *GImGui; IM_ASSERT(&g.IO == this && "Can only add events to current context."); - if ((wheel_x == 0.0f && wheel_y == 0.0f) || !AppAcceptingEvents) + + // Filter duplicate (unlike most events, wheel values are relative and easy to filter) + if (!AppAcceptingEvents || (wheel_x == 0.0f && wheel_y == 0.0f)) return; ImGuiInputEvent e; @@ -1403,6 +1491,12 @@ void ImGuiIO::AddFocusEvent(bool focused) ImGuiContext& g = *GImGui; IM_ASSERT(&g.IO == this && "Can only add events to current context."); + // Filter duplicate + const ImGuiInputEvent* latest_event = FindLatestInputEvent(ImGuiInputEventType_Focus); + const bool latest_focused = latest_event ? latest_event->AppFocused.Focused : !g.IO.AppFocusLost; + if (latest_focused == focused) + return; + ImGuiInputEvent e; e.Type = ImGuiInputEventType_Focus; e.AppFocused.Focused = focused; @@ -1537,14 +1631,14 @@ ImVec2 ImTriangleClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& c, int ImStricmp(const char* str1, const char* str2) { int d; - while ((d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; } + while ((d = ImToUpper(*str2) - ImToUpper(*str1)) == 0 && *str1) { str1++; str2++; } return d; } int ImStrnicmp(const char* str1, const char* str2, size_t count) { int d = 0; - while (count > 0 && (d = toupper(*str2) - toupper(*str1)) == 0 && *str1) { str1++; str2++; count--; } + while (count > 0 && (d = ImToUpper(*str2) - ImToUpper(*str1)) == 0 && *str1) { str1++; str2++; count--; } return d; } @@ -1611,14 +1705,14 @@ const char* ImStristr(const char* haystack, const char* haystack_end, const char if (!needle_end) needle_end = needle + strlen(needle); - const char un0 = (char)toupper(*needle); + const char un0 = (char)ImToUpper(*needle); while ((!haystack_end && *haystack) || (haystack_end && haystack < haystack_end)) { - if (toupper(*haystack) == un0) + if (ImToUpper(*haystack) == un0) { const char* b = needle + 1; for (const char* a = haystack + 1; b < needle_end; a++, b++) - if (toupper(*a) != toupper(*b)) + if (ImToUpper(*a) != ImToUpper(*b)) break; if (b == needle_end) return haystack; @@ -2382,7 +2476,7 @@ bool ImGuiTextFilter::PassFilter(const char* text, const char* text_end) const } //----------------------------------------------------------------------------- -// [SECTION] ImGuiTextBuffer +// [SECTION] ImGuiTextBuffer, ImGuiTextIndex //----------------------------------------------------------------------------- // On some platform vsnprintf() takes va_list by reference and modifies it. @@ -2450,6 +2544,20 @@ void ImGuiTextBuffer::appendfv(const char* fmt, va_list args) va_end(args_copy); } +void ImGuiTextIndex::append(const char* base, int old_size, int new_size) +{ + IM_ASSERT(old_size >= 0 && new_size >= old_size && new_size >= EndOffset); + if (old_size == new_size) + return; + if (EndOffset == 0 || base[EndOffset - 1] == '\n') + LineOffsets.push_back(EndOffset); + const char* base_end = base + new_size; + for (const char* p = base + old_size; (p = (const char*)memchr(p, '\n', base_end - p)) != 0; ) + if (++p < base_end) // Don't push a trailing offset on last \n + LineOffsets.push_back((int)(intptr_t)(p - base)); + EndOffset = ImMax(EndOffset, new_size); +} + //----------------------------------------------------------------------------- // [SECTION] ImGuiListClipper // This is currently not as flexible/powerful as it should be and really confusing/spaghetti, mostly because we changed @@ -2466,7 +2574,7 @@ static bool GetSkipItemForListClipping() #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS // Legacy helper to calculate coarse clipping of large list of evenly sized items. -// This legacy API is not ideal because it assume we will return a single contiguous rectangle. +// This legacy API is not ideal because it assumes we will return a single contiguous rectangle. // Prefer using ImGuiListClipper which can returns non-contiguous ranges. void ImGui::CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end) { @@ -2581,13 +2689,11 @@ ImGuiListClipper::~ImGuiListClipper() End(); } -// Use case A: Begin() called from constructor with items_height<0, then called again from Step() in StepNo 1 -// Use case B: Begin() called from constructor with items_height>0 -// FIXME-LEGACY: Ideally we should remove the Begin/End functions but they are part of the legacy API we still support. This is why some of the code in Step() calling Begin() and reassign some fields, spaghetti style. void ImGuiListClipper::Begin(int items_count, float items_height) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + IMGUI_DEBUG_LOG_CLIPPER("Clipper: Begin(%d,%.2f) in '%s'\n", items_count, items_height, window->Name); if (ImGuiTable* table = g.CurrentTable) if (table->IsInsideRow) @@ -2614,6 +2720,7 @@ void ImGuiListClipper::End() if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData) { // In theory here we should assert that we are already at the right position, but it seems saner to just seek at the end and not assert/crash the user. + IMGUI_DEBUG_LOG_CLIPPER("Clipper: End() in '%s'\n", g.CurrentWindow->Name); if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0) ImGuiListClipper_SeekCursorForItem(this, ItemsCount); @@ -2639,11 +2746,11 @@ void ImGuiListClipper::ForceDisplayRangeByIndices(int item_min, int item_max) data->Ranges.push_back(ImGuiListClipperRange::FromIndices(item_min, item_max)); } -bool ImGuiListClipper::Step() +static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - ImGuiListClipperData* data = (ImGuiListClipperData*)TempData; + ImGuiListClipperData* data = (ImGuiListClipperData*)clipper->TempData; IM_ASSERT(data != NULL && "Called ImGuiListClipper::Step() too many times, or before ImGuiListClipper::Begin() ?"); ImGuiTable* table = g.CurrentTable; @@ -2651,18 +2758,17 @@ bool ImGuiListClipper::Step() ImGui::TableEndRow(table); // No items - if (ItemsCount == 0 || GetSkipItemForListClipping()) - return (void)End(), false; + if (clipper->ItemsCount == 0 || GetSkipItemForListClipping()) + return false; // While we are in frozen row state, keep displaying items one by one, unclipped // FIXME: Could be stored as a table-agnostic state. if (data->StepNo == 0 && table != NULL && !table->IsUnfrozenRows) { - DisplayStart = data->ItemsFrozen; - DisplayEnd = data->ItemsFrozen + 1; - if (DisplayStart >= ItemsCount) - return (void)End(), false; - data->ItemsFrozen++; + clipper->DisplayStart = data->ItemsFrozen; + clipper->DisplayEnd = ImMin(data->ItemsFrozen + 1, clipper->ItemsCount); + if (clipper->DisplayStart < clipper->DisplayEnd) + data->ItemsFrozen++; return true; } @@ -2670,15 +2776,13 @@ bool ImGuiListClipper::Step() bool calc_clipping = false; if (data->StepNo == 0) { - StartPosY = window->DC.CursorPos.y; - if (ItemsHeight <= 0.0f) + clipper->StartPosY = window->DC.CursorPos.y; + if (clipper->ItemsHeight <= 0.0f) { // Submit the first item (or range) so we can measure its height (generally the first range is 0..1) data->Ranges.push_front(ImGuiListClipperRange::FromIndices(data->ItemsFrozen, data->ItemsFrozen + 1)); - DisplayStart = ImMax(data->Ranges[0].Min, data->ItemsFrozen); - DisplayEnd = ImMin(data->Ranges[0].Max, ItemsCount); - if (DisplayStart == DisplayEnd) - return (void)End(), false; + clipper->DisplayStart = ImMax(data->Ranges[0].Min, data->ItemsFrozen); + clipper->DisplayEnd = ImMin(data->Ranges[0].Max, clipper->ItemsCount); data->StepNo = 1; return true; } @@ -2686,29 +2790,29 @@ bool ImGuiListClipper::Step() } // Step 1: Let the clipper infer height from first range - if (ItemsHeight <= 0.0f) + if (clipper->ItemsHeight <= 0.0f) { IM_ASSERT(data->StepNo == 1); if (table) - IM_ASSERT(table->RowPosY1 == StartPosY && table->RowPosY2 == window->DC.CursorPos.y); + IM_ASSERT(table->RowPosY1 == clipper->StartPosY && table->RowPosY2 == window->DC.CursorPos.y); - ItemsHeight = (window->DC.CursorPos.y - StartPosY) / (float)(DisplayEnd - DisplayStart); - bool affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision(StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y); + clipper->ItemsHeight = (window->DC.CursorPos.y - clipper->StartPosY) / (float)(clipper->DisplayEnd - clipper->DisplayStart); + bool affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision(clipper->StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y); if (affected_by_floating_point_precision) - ItemsHeight = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y; // FIXME: Technically wouldn't allow multi-line entries. + clipper->ItemsHeight = window->DC.PrevLineSize.y + g.Style.ItemSpacing.y; // FIXME: Technically wouldn't allow multi-line entries. - IM_ASSERT(ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!"); + IM_ASSERT(clipper->ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!"); calc_clipping = true; // If item height had to be calculated, calculate clipping afterwards. } // Step 0 or 1: Calculate the actual ranges of visible elements. - const int already_submitted = DisplayEnd; + const int already_submitted = clipper->DisplayEnd; if (calc_clipping) { if (g.LogEnabled) { // If logging is active, do not perform any clipping - data->Ranges.push_back(ImGuiListClipperRange::FromIndices(0, ItemsCount)); + data->Ranges.push_back(ImGuiListClipperRange::FromIndices(0, clipper->ItemsCount)); } else { @@ -2717,7 +2821,7 @@ bool ImGuiListClipper::Step() if (is_nav_request) data->Ranges.push_back(ImGuiListClipperRange::FromPositions(g.NavScoringNoClipRect.Min.y, g.NavScoringNoClipRect.Max.y, 0, 0)); if (is_nav_request && (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) && g.NavTabbingDir == -1) - data->Ranges.push_back(ImGuiListClipperRange::FromIndices(ItemsCount - 1, ItemsCount)); + data->Ranges.push_back(ImGuiListClipperRange::FromIndices(clipper->ItemsCount - 1, clipper->ItemsCount)); // Add focused/active item ImRect nav_rect_abs = ImGui::WindowRectRelToAbs(window, window->NavRectRel[0]); @@ -2737,10 +2841,10 @@ bool ImGuiListClipper::Step() for (int i = 0; i < data->Ranges.Size; i++) if (data->Ranges[i].PosToIndexConvert) { - int m1 = (int)(((double)data->Ranges[i].Min - window->DC.CursorPos.y - data->LossynessOffset) / ItemsHeight); - int m2 = (int)((((double)data->Ranges[i].Max - window->DC.CursorPos.y - data->LossynessOffset) / ItemsHeight) + 0.999999f); - data->Ranges[i].Min = ImClamp(already_submitted + m1 + data->Ranges[i].PosToIndexOffsetMin, already_submitted, ItemsCount - 1); - data->Ranges[i].Max = ImClamp(already_submitted + m2 + data->Ranges[i].PosToIndexOffsetMax, data->Ranges[i].Min + 1, ItemsCount); + int m1 = (int)(((double)data->Ranges[i].Min - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight); + int m2 = (int)((((double)data->Ranges[i].Max - window->DC.CursorPos.y - data->LossynessOffset) / clipper->ItemsHeight) + 0.999999f); + data->Ranges[i].Min = ImClamp(already_submitted + m1 + data->Ranges[i].PosToIndexOffsetMin, already_submitted, clipper->ItemsCount - 1); + data->Ranges[i].Max = ImClamp(already_submitted + m2 + data->Ranges[i].PosToIndexOffsetMax, data->Ranges[i].Min + 1, clipper->ItemsCount); data->Ranges[i].PosToIndexConvert = false; } ImGuiListClipper_SortAndFuseRanges(data->Ranges, data->StepNo); @@ -2749,23 +2853,45 @@ bool ImGuiListClipper::Step() // Step 0+ (if item height is given in advance) or 1+: Display the next range in line. if (data->StepNo < data->Ranges.Size) { - DisplayStart = ImMax(data->Ranges[data->StepNo].Min, already_submitted); - DisplayEnd = ImMin(data->Ranges[data->StepNo].Max, ItemsCount); - if (DisplayStart > already_submitted) //-V1051 - ImGuiListClipper_SeekCursorForItem(this, DisplayStart); + clipper->DisplayStart = ImMax(data->Ranges[data->StepNo].Min, already_submitted); + clipper->DisplayEnd = ImMin(data->Ranges[data->StepNo].Max, clipper->ItemsCount); + if (clipper->DisplayStart > already_submitted) //-V1051 + ImGuiListClipper_SeekCursorForItem(clipper, clipper->DisplayStart); data->StepNo++; return true; } // After the last step: Let the clipper validate that we have reached the expected Y position (corresponding to element DisplayEnd), // Advance the cursor to the end of the list and then returns 'false' to end the loop. - if (ItemsCount < INT_MAX) - ImGuiListClipper_SeekCursorForItem(this, ItemsCount); + if (clipper->ItemsCount < INT_MAX) + ImGuiListClipper_SeekCursorForItem(clipper, clipper->ItemsCount); - End(); return false; } +bool ImGuiListClipper::Step() +{ + ImGuiContext& g = *GImGui; + bool need_items_height = (ItemsHeight <= 0.0f); + bool ret = ImGuiListClipper_StepInternal(this); + if (ret && (DisplayStart == DisplayEnd)) + ret = false; + if (g.CurrentTable && g.CurrentTable->IsUnfrozenRows == false) + IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): inside frozen table row.\n"); + if (need_items_height && ItemsHeight > 0.0f) + IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): computed ItemsHeight: %.2f.\n", ItemsHeight); + if (ret) + { + IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): display %d to %d.\n", DisplayStart, DisplayEnd); + } + else + { + IMGUI_DEBUG_LOG_CLIPPER("Clipper: Step(): End.\n"); + End(); + } + return ret; +} + //----------------------------------------------------------------------------- // [SECTION] STYLING //----------------------------------------------------------------------------- @@ -2832,6 +2958,11 @@ void ImGui::PushStyleColor(ImGuiCol idx, const ImVec4& col) void ImGui::PopStyleColor(int count) { ImGuiContext& g = *GImGui; + if (g.ColorStack.Size < count) + { + IM_ASSERT_USER_ERROR(g.ColorStack.Size > count, "Calling PopStyleColor() too many times: stack underflow."); + count = g.ColorStack.Size; + } while (count > 0) { ImGuiColorMod& backup = g.ColorStack.back(); @@ -2916,6 +3047,11 @@ void ImGui::PushStyleVar(ImGuiStyleVar idx, const ImVec2& val) void ImGui::PopStyleVar(int count) { ImGuiContext& g = *GImGui; + if (g.StyleVarStack.Size < count) + { + IM_ASSERT_USER_ERROR(g.StyleVarStack.Size > count, "Calling PopStyleVar() too many times: stack underflow."); + count = g.StyleVarStack.Size; + } while (count > 0) { // We avoid a generic memcpy(data, &backup.Backup.., GDataTypeSize[info->Type] * info->Count), the overhead in Debug is not worth it. @@ -3264,6 +3400,215 @@ void ImGui::RenderMouseCursor(ImVec2 base_pos, float base_scale, ImGuiMouseCurso } } +//----------------------------------------------------------------------------- +// [SECTION] INITIALIZATION, SHUTDOWN +//----------------------------------------------------------------------------- + +// Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself +// Note that we still point to some static data and members (such as GFontAtlas), so the state instance you end up using will point to the static data within its module +ImGuiContext* ImGui::GetCurrentContext() +{ + return GImGui; +} + +void ImGui::SetCurrentContext(ImGuiContext* ctx) +{ +#ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC + IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this. +#else + GImGui = ctx; +#endif +} + +void ImGui::SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data) +{ + GImAllocatorAllocFunc = alloc_func; + GImAllocatorFreeFunc = free_func; + GImAllocatorUserData = user_data; +} + +// This is provided to facilitate copying allocators from one static/DLL boundary to another (e.g. retrieve default allocator of your executable address space) +void ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data) +{ + *p_alloc_func = GImAllocatorAllocFunc; + *p_free_func = GImAllocatorFreeFunc; + *p_user_data = GImAllocatorUserData; +} + +ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas) +{ + ImGuiContext* prev_ctx = GetCurrentContext(); + ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas); + SetCurrentContext(ctx); + Initialize(); + if (prev_ctx != NULL) + SetCurrentContext(prev_ctx); // Restore previous context if any, else keep new one. + return ctx; +} + +void ImGui::DestroyContext(ImGuiContext* ctx) +{ + ImGuiContext* prev_ctx = GetCurrentContext(); + if (ctx == NULL) //-V1051 + ctx = prev_ctx; + SetCurrentContext(ctx); + Shutdown(); + SetCurrentContext((prev_ctx != ctx) ? prev_ctx : NULL); + IM_DELETE(ctx); +} + +// IMPORTANT: ###xxx suffixes must be same in ALL languages +static const ImGuiLocEntry GLocalizationEntriesEnUS[] = +{ + { ImGuiLocKey_TableSizeOne, "Size column to fit###SizeOne" }, + { ImGuiLocKey_TableSizeAllFit, "Size all columns to fit###SizeAll" }, + { ImGuiLocKey_TableSizeAllDefault, "Size all columns to default###SizeAll" }, + { ImGuiLocKey_TableResetOrder, "Reset order###ResetOrder" }, + { ImGuiLocKey_WindowingMainMenuBar, "(Main menu bar)" }, + { ImGuiLocKey_WindowingPopup, "(Popup)" }, + { ImGuiLocKey_WindowingUntitled, "(Untitled)" }, +}; + +void ImGui::Initialize() +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(!g.Initialized && !g.SettingsLoaded); + + // Add .ini handle for ImGuiWindow and ImGuiTable types + { + ImGuiSettingsHandler ini_handler; + ini_handler.TypeName = "Window"; + ini_handler.TypeHash = ImHashStr("Window"); + ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll; + ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen; + ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine; + ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll; + ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll; + AddSettingsHandler(&ini_handler); + } + TableSettingsAddSettingsHandler(); + + // Setup default localization table + LocalizeRegisterEntries(GLocalizationEntriesEnUS, IM_ARRAYSIZE(GLocalizationEntriesEnUS)); + + // Create default viewport + ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(); + g.Viewports.push_back(viewport); + g.TempBuffer.resize(1024 * 3 + 1, 0); + +#ifdef IMGUI_HAS_DOCK +#endif + + g.Initialized = true; +} + +// This function is merely here to free heap allocations. +void ImGui::Shutdown() +{ + // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame) + ImGuiContext& g = *GImGui; + if (g.IO.Fonts && g.FontAtlasOwnedByContext) + { + g.IO.Fonts->Locked = false; + IM_DELETE(g.IO.Fonts); + } + g.IO.Fonts = NULL; + g.DrawListSharedData.TempBuffer.clear(); + + // Cleanup of other data are conditional on actually having initialized Dear ImGui. + if (!g.Initialized) + return; + + // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file) + if (g.SettingsLoaded && g.IO.IniFilename != NULL) + SaveIniSettingsToDisk(g.IO.IniFilename); + + CallContextHooks(&g, ImGuiContextHookType_Shutdown); + + // Clear everything else + g.Windows.clear_delete(); + g.WindowsFocusOrder.clear(); + g.WindowsTempSortBuffer.clear(); + g.CurrentWindow = NULL; + g.CurrentWindowStack.clear(); + g.WindowsById.Clear(); + g.NavWindow = NULL; + g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL; + g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL; + g.MovingWindow = NULL; + + g.KeysRoutingTable.Clear(); + + g.ColorStack.clear(); + g.StyleVarStack.clear(); + g.FontStack.clear(); + g.OpenPopupStack.clear(); + g.BeginPopupStack.clear(); + + g.Viewports.clear_delete(); + + g.TabBars.Clear(); + g.CurrentTabBarStack.clear(); + g.ShrinkWidthBuffer.clear(); + + g.ClipperTempData.clear_destruct(); + + g.Tables.Clear(); + g.TablesTempData.clear_destruct(); + g.DrawChannelsTempMergeBuffer.clear(); + + g.ClipboardHandlerData.clear(); + g.MenusIdSubmittedThisFrame.clear(); + g.InputTextState.ClearFreeMemory(); + + g.SettingsWindows.clear(); + g.SettingsHandlers.clear(); + + if (g.LogFile) + { +#ifndef IMGUI_DISABLE_TTY_FUNCTIONS + if (g.LogFile != stdout) +#endif + ImFileClose(g.LogFile); + g.LogFile = NULL; + } + g.LogBuffer.clear(); + g.DebugLogBuf.clear(); + g.DebugLogIndex.clear(); + + g.Initialized = false; +} + +// No specific ordering/dependency support, will see as needed +ImGuiID ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook) +{ + ImGuiContext& g = *ctx; + IM_ASSERT(hook->Callback != NULL && hook->HookId == 0 && hook->Type != ImGuiContextHookType_PendingRemoval_); + g.Hooks.push_back(*hook); + g.Hooks.back().HookId = ++g.HookIdNext; + return g.HookIdNext; +} + +// Deferred removal, avoiding issue with changing vector while iterating it +void ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id) +{ + ImGuiContext& g = *ctx; + IM_ASSERT(hook_id != 0); + for (int n = 0; n < g.Hooks.Size; n++) + if (g.Hooks[n].HookId == hook_id) + g.Hooks[n].Type = ImGuiContextHookType_PendingRemoval_; +} + +// Call context hooks (used by e.g. test engine) +// We assume a small number of hooks so all stored in same array +void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type) +{ + ImGuiContext& g = *ctx; + for (int n = 0; n < g.Hooks.Size; n++) + if (g.Hooks[n].Type == hook_type) + g.Hooks[n].Callback(&g, &g.Hooks[n]); +} + //----------------------------------------------------------------------------- // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) @@ -3423,10 +3768,11 @@ void ImGui::SetActiveID(ImGuiID id, ImGuiWindow* window) // Clear declaration of inputs claimed by the widget // (Please note that this is WIP and not all keys/inputs are thoroughly declared by all widgets yet) - g.ActiveIdUsingMouseWheel = false; g.ActiveIdUsingNavDirMask = 0x00; + g.ActiveIdUsingAllKeyboardKeys = false; +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO g.ActiveIdUsingNavInputMask = 0x00; - g.ActiveIdUsingKeyInputMask.ClearAllBits(); +#endif } void ImGui::ClearActiveID() @@ -3439,7 +3785,6 @@ void ImGui::SetHoveredID(ImGuiID id) ImGuiContext& g = *GImGui; g.HoveredId = id; g.HoveredIdAllowOverlap = false; - g.HoveredIdUsingMouseWheel = false; if (id != 0 && g.HoveredIdPreviousFrame != id) g.HoveredIdTimer = g.HoveredIdNotActiveTimer = 0.0f; } @@ -3464,7 +3809,7 @@ void ImGui::KeepAliveID(ImGuiID id) void ImGui::MarkItemEdited(ImGuiID id) { // This marking is solely to be able to provide info for IsItemDeactivatedAfterEdit(). - // ActiveId might have been released by the time we call this (as in the typical press/release button behavior) but still need need to fill the data. + // ActiveId might have been released by the time we call this (as in the typical press/release button behavior) but still need to fill the data. ImGuiContext& g = *GImGui; IM_ASSERT(g.ActiveId == id || g.ActiveId == 0 || g.DragDropActive); IM_UNUSED(id); // Avoid unused variable warnings when asserts are compiled out. @@ -3484,11 +3829,17 @@ static inline bool IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFla if (focused_root_window->WasActive && focused_root_window != window->RootWindow) { // For the purpose of those flags we differentiate "standard popup" from "modal popup" - // NB: The order of those two tests is important because Modal windows are also Popups. + // NB: The 'else' is important because Modal windows are also Popups. + bool want_inhibit = false; if (focused_root_window->Flags & ImGuiWindowFlags_Modal) - return false; - if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup)) - return false; + want_inhibit = true; + else if ((focused_root_window->Flags & ImGuiWindowFlags_Popup) && !(flags & ImGuiHoveredFlags_AllowWhenBlockedByPopup)) + want_inhibit = true; + + // Inhibit hover unless the window is within the stack of our modal/popup + if (want_inhibit) + if (!ImGui::IsWindowWithinBeginStackOf(window->RootWindow, focused_root_window)) + return false; } return true; } @@ -3515,6 +3866,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) return false; IM_ASSERT((flags & (ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy)) == 0); // Flags not supported by this function + // Done with rectangle culling so we can perform heavier checks now // Test if we are hovering the right window (our window could be behind another window) // [2021/03/02] Reworked / reverted the revert, finally. Note we want e.g. BeginGroup/ItemAdd/EndGroup to work as well. (#3851) // [2017/10/16] Reverted commit 344d48be3 and testing RootWindow instead. I believe it is correct to NOT test for RootWindow but this leaves us unable @@ -3531,7 +3883,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) // Test if interactions on this window are blocked by an active popup or modal. // The ImGuiHoveredFlags_AllowWhenBlockedByPopup flag will be tested here. - if (!IsWindowContentHoverable(window, flags)) + if (!IsWindowContentHoverable(window, flags) && !(g.LastItemData.InFlags & ImGuiItemFlags_NoWindowHoverableCheck)) return false; // Test if the item is disabled @@ -3544,6 +3896,24 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) return false; } + // Handle hover delay + // (some ideas: https://www.nngroup.com/articles/timing-exposing-content) + float delay; + if (flags & ImGuiHoveredFlags_DelayNormal) + delay = g.IO.HoverDelayNormal; + else if (flags & ImGuiHoveredFlags_DelayShort) + delay = g.IO.HoverDelayShort; + else + delay = 0.0f; + if (delay > 0.0f) + { + ImGuiID hover_delay_id = (g.LastItemData.ID != 0) ? g.LastItemData.ID : window->GetIDFromRectangle(g.LastItemData.Rect); + if ((flags & ImGuiHoveredFlags_NoSharedDelay) && (g.HoverDelayIdPreviousFrame != hover_delay_id)) + g.HoverDelayTimer = 0.0f; + g.HoverDelayId = hover_delay_id; + return g.HoverDelayTimer >= delay; + } + return true; } @@ -3561,7 +3931,10 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) return false; if (!IsMouseHoveringRect(bb.Min, bb.Max)) return false; - if (!IsWindowContentHoverable(window, ImGuiHoveredFlags_None)) + + // Done with rectangle culling so we can perform heavier checks now. + ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.InFlags : g.CurrentItemFlags); + if (!(item_flags & ImGuiItemFlags_NoWindowHoverableCheck) && !IsWindowContentHoverable(window, ImGuiHoveredFlags_None)) { g.HoveredIdDisabled = true; return false; @@ -3573,7 +3946,6 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) SetHoveredID(id); // When disabled we'll return false but still set HoveredId - ImGuiItemFlags item_flags = (g.LastItemData.ID == id ? g.LastItemData.InFlags : g.CurrentItemFlags); if (item_flags & ImGuiItemFlags_Disabled) { // Release active id if turning disabled @@ -3588,8 +3960,7 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) // [DEBUG] Item Picker tool! // We perform the check here because SetHoveredID() is not frequently called (1~ time a frame), making // the cost of this tool near-zero. We can get slightly better call-stack and support picking non-hovered - // items if we perform the test in ItemAdd(), but that would incur a small runtime cost. - // #define IMGUI_DEBUG_TOOL_ITEM_PICKER_EX in imconfig.h if you want this check to also be performed in ItemAdd(). + // items if we performed the test in ItemAdd(), but that would incur a small runtime cost. if (g.DebugItemPickerActive && g.HoveredIdPreviousFrame == id) GetForegroundDrawList()->AddRect(bb.Min, bb.Max, IM_COL32(255, 255, 0, 255)); if (g.DebugItemPickerBreakId == id) @@ -3602,6 +3973,7 @@ bool ImGui::ItemHoverable(const ImRect& bb, ImGuiID id) return true; } +// FIXME: This is inlined/duplicated in ItemAdd() bool ImGui::IsClippedEx(const ImRect& bb, ImGuiID id) { ImGuiContext& g = *GImGui; @@ -3683,89 +4055,6 @@ const char* ImGui::GetVersion() return IMGUI_VERSION; } -// Internal state access - if you want to share Dear ImGui state between modules (e.g. DLL) or allocate it yourself -// Note that we still point to some static data and members (such as GFontAtlas), so the state instance you end up using will point to the static data within its module -ImGuiContext* ImGui::GetCurrentContext() -{ - return GImGui; -} - -void ImGui::SetCurrentContext(ImGuiContext* ctx) -{ -#ifdef IMGUI_SET_CURRENT_CONTEXT_FUNC - IMGUI_SET_CURRENT_CONTEXT_FUNC(ctx); // For custom thread-based hackery you may want to have control over this. -#else - GImGui = ctx; -#endif -} - -void ImGui::SetAllocatorFunctions(ImGuiMemAllocFunc alloc_func, ImGuiMemFreeFunc free_func, void* user_data) -{ - GImAllocatorAllocFunc = alloc_func; - GImAllocatorFreeFunc = free_func; - GImAllocatorUserData = user_data; -} - -// This is provided to facilitate copying allocators from one static/DLL boundary to another (e.g. retrieve default allocator of your executable address space) -void ImGui::GetAllocatorFunctions(ImGuiMemAllocFunc* p_alloc_func, ImGuiMemFreeFunc* p_free_func, void** p_user_data) -{ - *p_alloc_func = GImAllocatorAllocFunc; - *p_free_func = GImAllocatorFreeFunc; - *p_user_data = GImAllocatorUserData; -} - -ImGuiContext* ImGui::CreateContext(ImFontAtlas* shared_font_atlas) -{ - ImGuiContext* prev_ctx = GetCurrentContext(); - ImGuiContext* ctx = IM_NEW(ImGuiContext)(shared_font_atlas); - SetCurrentContext(ctx); - Initialize(); - if (prev_ctx != NULL) - SetCurrentContext(prev_ctx); // Restore previous context if any, else keep new one. - return ctx; -} - -void ImGui::DestroyContext(ImGuiContext* ctx) -{ - ImGuiContext* prev_ctx = GetCurrentContext(); - if (ctx == NULL) //-V1051 - ctx = prev_ctx; - SetCurrentContext(ctx); - Shutdown(); - SetCurrentContext((prev_ctx != ctx) ? prev_ctx : NULL); - IM_DELETE(ctx); -} - -// No specific ordering/dependency support, will see as needed -ImGuiID ImGui::AddContextHook(ImGuiContext* ctx, const ImGuiContextHook* hook) -{ - ImGuiContext& g = *ctx; - IM_ASSERT(hook->Callback != NULL && hook->HookId == 0 && hook->Type != ImGuiContextHookType_PendingRemoval_); - g.Hooks.push_back(*hook); - g.Hooks.back().HookId = ++g.HookIdNext; - return g.HookIdNext; -} - -// Deferred removal, avoiding issue with changing vector while iterating it -void ImGui::RemoveContextHook(ImGuiContext* ctx, ImGuiID hook_id) -{ - ImGuiContext& g = *ctx; - IM_ASSERT(hook_id != 0); - for (int n = 0; n < g.Hooks.Size; n++) - if (g.Hooks[n].HookId == hook_id) - g.Hooks[n].Type = ImGuiContextHookType_PendingRemoval_; -} - -// Call context hooks (used by e.g. test engine) -// We assume a small number of hooks so all stored in same array -void ImGui::CallContextHooks(ImGuiContext* ctx, ImGuiContextHookType hook_type) -{ - ImGuiContext& g = *ctx; - for (int n = 0; n < g.Hooks.Size; n++) - if (g.Hooks[n].Type == hook_type) - g.Hooks[n].Callback(&g, &g.Hooks[n]); -} - ImGuiIO& ImGui::GetIO() { IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); @@ -3852,7 +4141,7 @@ void ImGui::StartMouseMovingWindow(ImGuiWindow* window) g.NavDisableHighlight = true; g.ActiveIdClickOffset = g.IO.MouseClickedPos[0] - window->RootWindow->Pos; g.ActiveIdNoClearOnFocusLoss = true; - SetActiveIdUsingNavAndKeys(); + SetActiveIdUsingAllKeyboardKeys(); bool can_move_window = true; if ((window->Flags & ImGuiWindowFlags_NoMove) || (window->RootWindow->Flags & ImGuiWindowFlags_NoMove)) @@ -3959,235 +4248,6 @@ static bool IsWindowActiveAndVisible(ImGuiWindow* window) return (window->Active) && (!window->Hidden); } -static void ImGui::UpdateKeyboardInputs() -{ - ImGuiContext& g = *GImGui; - ImGuiIO& io = g.IO; - - // Import legacy keys or verify they are not used -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - if (io.BackendUsingLegacyKeyArrays == 0) - { - // Backend used new io.AddKeyEvent() API: Good! Verify that old arrays are never written to externally. - for (int n = 0; n < ImGuiKey_LegacyNativeKey_END; n++) - IM_ASSERT((io.KeysDown[n] == false || IsKeyDown(n)) && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!"); - } - else - { - if (g.FrameCount == 0) - for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++) - IM_ASSERT(g.IO.KeyMap[n] == -1 && "Backend is not allowed to write to io.KeyMap[0..511]!"); - - // Build reverse KeyMap (Named -> Legacy) - for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++) - if (io.KeyMap[n] != -1) - { - IM_ASSERT(IsLegacyKey((ImGuiKey)io.KeyMap[n])); - io.KeyMap[io.KeyMap[n]] = n; - } - - // Import legacy keys into new ones - for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++) - if (io.KeysDown[n] || io.BackendUsingLegacyKeyArrays == 1) - { - const ImGuiKey key = (ImGuiKey)(io.KeyMap[n] != -1 ? io.KeyMap[n] : n); - IM_ASSERT(io.KeyMap[n] == -1 || IsNamedKey(key)); - io.KeysData[key].Down = io.KeysDown[n]; - if (key != n) - io.KeysDown[key] = io.KeysDown[n]; // Allow legacy code using io.KeysDown[GetKeyIndex()] with old backends - io.BackendUsingLegacyKeyArrays = 1; - } - if (io.BackendUsingLegacyKeyArrays == 1) - { - io.KeysData[ImGuiKey_ModCtrl].Down = io.KeyCtrl; - io.KeysData[ImGuiKey_ModShift].Down = io.KeyShift; - io.KeysData[ImGuiKey_ModAlt].Down = io.KeyAlt; - io.KeysData[ImGuiKey_ModSuper].Down = io.KeySuper; - } - } -#endif - - // Synchronize io.KeyMods with individual modifiers io.KeyXXX bools - io.KeyMods = GetMergedModFlags(); - - // Clear gamepad data if disabled - if ((io.BackendFlags & ImGuiBackendFlags_HasGamepad) == 0) - for (int i = ImGuiKey_Gamepad_BEGIN; i < ImGuiKey_Gamepad_END; i++) - { - io.KeysData[i - ImGuiKey_KeysData_OFFSET].Down = false; - io.KeysData[i - ImGuiKey_KeysData_OFFSET].AnalogValue = 0.0f; - } - - // Update keys - for (int i = 0; i < IM_ARRAYSIZE(io.KeysData); i++) - { - ImGuiKeyData* key_data = &io.KeysData[i]; - key_data->DownDurationPrev = key_data->DownDuration; - key_data->DownDuration = key_data->Down ? (key_data->DownDuration < 0.0f ? 0.0f : key_data->DownDuration + io.DeltaTime) : -1.0f; - } -} - -static void ImGui::UpdateMouseInputs() -{ - ImGuiContext& g = *GImGui; - ImGuiIO& io = g.IO; - - // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well) - if (IsMousePosValid(&io.MousePos)) - io.MousePos = g.MouseLastValidPos = ImFloorSigned(io.MousePos); - - // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta - if (IsMousePosValid(&io.MousePos) && IsMousePosValid(&io.MousePosPrev)) - io.MouseDelta = io.MousePos - io.MousePosPrev; - else - io.MouseDelta = ImVec2(0.0f, 0.0f); - - // If mouse moved we re-enable mouse hovering in case it was disabled by gamepad/keyboard. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true. - if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f) - g.NavDisableMouseHover = false; - - io.MousePosPrev = io.MousePos; - for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) - { - io.MouseClicked[i] = io.MouseDown[i] && io.MouseDownDuration[i] < 0.0f; - io.MouseClickedCount[i] = 0; // Will be filled below - io.MouseReleased[i] = !io.MouseDown[i] && io.MouseDownDuration[i] >= 0.0f; - io.MouseDownDurationPrev[i] = io.MouseDownDuration[i]; - io.MouseDownDuration[i] = io.MouseDown[i] ? (io.MouseDownDuration[i] < 0.0f ? 0.0f : io.MouseDownDuration[i] + io.DeltaTime) : -1.0f; - if (io.MouseClicked[i]) - { - bool is_repeated_click = false; - if ((float)(g.Time - io.MouseClickedTime[i]) < io.MouseDoubleClickTime) - { - ImVec2 delta_from_click_pos = IsMousePosValid(&io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); - if (ImLengthSqr(delta_from_click_pos) < io.MouseDoubleClickMaxDist * io.MouseDoubleClickMaxDist) - is_repeated_click = true; - } - if (is_repeated_click) - io.MouseClickedLastCount[i]++; - else - io.MouseClickedLastCount[i] = 1; - io.MouseClickedTime[i] = g.Time; - io.MouseClickedPos[i] = io.MousePos; - io.MouseClickedCount[i] = io.MouseClickedLastCount[i]; - io.MouseDragMaxDistanceSqr[i] = 0.0f; - } - else if (io.MouseDown[i]) - { - // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold - float delta_sqr_click_pos = IsMousePosValid(&io.MousePos) ? ImLengthSqr(io.MousePos - io.MouseClickedPos[i]) : 0.0f; - io.MouseDragMaxDistanceSqr[i] = ImMax(io.MouseDragMaxDistanceSqr[i], delta_sqr_click_pos); - } - - // We provide io.MouseDoubleClicked[] as a legacy service - io.MouseDoubleClicked[i] = (io.MouseClickedCount[i] == 2); - - // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation - if (io.MouseClicked[i]) - g.NavDisableMouseHover = false; - } -} - -static void StartLockWheelingWindow(ImGuiWindow* window) -{ - ImGuiContext& g = *GImGui; - if (g.WheelingWindow == window) - return; - g.WheelingWindow = window; - g.WheelingWindowRefMousePos = g.IO.MousePos; - g.WheelingWindowTimer = WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER; -} - -void ImGui::UpdateMouseWheel() -{ - ImGuiContext& g = *GImGui; - - // Reset the locked window if we move the mouse or after the timer elapses - if (g.WheelingWindow != NULL) - { - g.WheelingWindowTimer -= g.IO.DeltaTime; - if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold) - g.WheelingWindowTimer = 0.0f; - if (g.WheelingWindowTimer <= 0.0f) - { - g.WheelingWindow = NULL; - g.WheelingWindowTimer = 0.0f; - } - } - - float wheel_x = g.IO.MouseWheelH; - float wheel_y = g.IO.MouseWheel; - if (wheel_x == 0.0f && wheel_y == 0.0f) - return; - - if ((g.ActiveId != 0 && g.ActiveIdUsingMouseWheel) || (g.HoveredIdPreviousFrame != 0 && g.HoveredIdPreviousFrameUsingMouseWheel)) - return; - - ImGuiWindow* window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow; - if (!window || window->Collapsed) - return; - - // Zoom / Scale window - // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned. - if (wheel_y != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling) - { - StartLockWheelingWindow(window); - const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f); - const float scale = new_font_scale / window->FontWindowScale; - window->FontWindowScale = new_font_scale; - if (window == window->RootWindow) - { - const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size; - SetWindowPos(window, window->Pos + offset, 0); - window->Size = ImFloor(window->Size * scale); - window->SizeFull = ImFloor(window->SizeFull * scale); - } - return; - } - - // Mouse wheel scrolling - // If a child window has the ImGuiWindowFlags_NoScrollWithMouse flag, we give a chance to scroll its parent - if (g.IO.KeyCtrl) - return; - - // As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead - // (we avoid doing it on OSX as it the OS input layer handles this already) - const bool swap_axis = g.IO.KeyShift && !g.IO.ConfigMacOSXBehaviors; - if (swap_axis) - { - wheel_x = wheel_y; - wheel_y = 0.0f; - } - - // Vertical Mouse Wheel scrolling - if (wheel_y != 0.0f) - { - StartLockWheelingWindow(window); - while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.y == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)))) - window = window->ParentWindow; - if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)) - { - float max_step = window->InnerRect.GetHeight() * 0.67f; - float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step)); - SetScrollY(window, window->Scroll.y - wheel_y * scroll_step); - } - } - - // Horizontal Mouse Wheel scrolling, or Vertical Mouse Wheel w/ Shift held - if (wheel_x != 0.0f) - { - StartLockWheelingWindow(window); - while ((window->Flags & ImGuiWindowFlags_ChildWindow) && ((window->ScrollMax.x == 0.0f) || ((window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)))) - window = window->ParentWindow; - if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)) - { - float max_step = window->InnerRect.GetWidth() * 0.67f; - float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step)); - SetScrollX(window, window->Scroll.x - wheel_x * scroll_step); - } - } -} - // The reason this is exposed in imgui_internal.h is: on touch-based system that don't have hovering, we want to dispatch inputs to the right target (imgui vs imgui+app) void ImGui::UpdateHoveredWindowAndCaptureFlags() { @@ -4265,18 +4325,6 @@ void ImGui::UpdateHoveredWindowAndCaptureFlags() io.WantTextInput = (g.WantTextInputNextFrame != -1) ? (g.WantTextInputNextFrame != 0) : false; } -// [Internal] Do not use directly (can read io.KeyMods instead) -ImGuiModFlags ImGui::GetMergedModFlags() -{ - ImGuiContext& g = *GImGui; - ImGuiModFlags key_mods = ImGuiModFlags_None; - if (g.IO.KeyCtrl) { key_mods |= ImGuiModFlags_Ctrl; } - if (g.IO.KeyShift) { key_mods |= ImGuiModFlags_Shift; } - if (g.IO.KeyAlt) { key_mods |= ImGuiModFlags_Alt; } - if (g.IO.KeySuper) { key_mods |= ImGuiModFlags_Super; } - return key_mods; -} - void ImGui::NewFrame() { IM_ASSERT(GImGui != NULL && "No current context. Did you call ImGui::CreateContext() and ImGui::SetCurrentContext() ?"); @@ -4353,10 +4401,8 @@ void ImGui::NewFrame() if (g.HoveredId && g.ActiveId != g.HoveredId) g.HoveredIdNotActiveTimer += g.IO.DeltaTime; g.HoveredIdPreviousFrame = g.HoveredId; - g.HoveredIdPreviousFrameUsingMouseWheel = g.HoveredIdUsingMouseWheel; g.HoveredId = 0; g.HoveredIdAllowOverlap = false; - g.HoveredIdUsingMouseWheel = false; g.HoveredIdDisabled = false; // Clear ActiveID if the item is not alive anymore. @@ -4384,8 +4430,41 @@ void ImGui::NewFrame() if (g.ActiveId == 0) { g.ActiveIdUsingNavDirMask = 0x00; + g.ActiveIdUsingAllKeyboardKeys = false; +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO g.ActiveIdUsingNavInputMask = 0x00; - g.ActiveIdUsingKeyInputMask.ClearAllBits(); +#endif + } + +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + if (g.ActiveId == 0) + g.ActiveIdUsingNavInputMask = 0; + else if (g.ActiveIdUsingNavInputMask != 0) + { + // If your custom widget code used: { g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel); } + // Since IMGUI_VERSION_NUM >= 18804 it should be: { SetKeyOwner(ImGuiKey_Escape, g.ActiveId); SetKeyOwner(ImGuiKey_NavGamepadCancel, g.ActiveId); } + if (g.ActiveIdUsingNavInputMask & (1 << ImGuiNavInput_Cancel)) + SetKeyOwner(ImGuiKey_Escape, g.ActiveId); + if (g.ActiveIdUsingNavInputMask & ~(1 << ImGuiNavInput_Cancel)) + IM_ASSERT(0); // Other values unsupported + } +#endif + + // Update hover delay for IsItemHovered() with delays and tooltips + g.HoverDelayIdPreviousFrame = g.HoverDelayId; + if (g.HoverDelayId != 0) + { + //if (g.IO.MouseDelta.x == 0.0f && g.IO.MouseDelta.y == 0.0f) // Need design/flags + g.HoverDelayTimer += g.IO.DeltaTime; + g.HoverDelayClearTimer = 0.0f; + g.HoverDelayId = 0; + } + else if (g.HoverDelayTimer > 0.0f) + { + // This gives a little bit of leeway before clearing the hover timer, allowing mouse to cross gaps + g.HoverDelayClearTimer += g.IO.DeltaTime; + if (g.HoverDelayClearTimer >= ImMax(0.20f, g.IO.DeltaTime * 2.0f)) // ~6 frames at 30 Hz + allow for low framerate + g.HoverDelayTimer = g.HoverDelayClearTimer = 0.0f; // May want a decaying timer, in which case need to clamp at max first, based on max of caller last requested timer. } // Drag and drop @@ -4448,9 +4527,10 @@ void ImGui::NewFrame() { ImGuiWindow* window = g.Windows[i]; window->WasActive = window->Active; - window->BeginCount = 0; window->Active = false; window->WriteAccessed = false; + window->BeginCountPreviousFrame = window->BeginCount; + window->BeginCount = 0; // Garbage collect transient buffers of recently unused windows if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time) @@ -4483,10 +4563,12 @@ void ImGui::NewFrame() // [DEBUG] Update debug features UpdateDebugToolItemPicker(); UpdateDebugToolStackQueries(); + if (g.DebugLocateFrames > 0 && --g.DebugLocateFrames == 0) + g.DebugLocateId = 0; // Create implicit/fallback window - which we will only render it if the user has added something to it. // We don't use "Debug" to avoid colliding with user trying to create a "Debug" window with custom flags. - // This fallback is particularly important as it avoid ImGui:: calls from crashing. + // This fallback is particularly important as it prevents ImGui:: calls from crashing. g.WithinFrameScopeWithImplicitWindow = true; SetNextWindowSize(ImVec2(400, 400), ImGuiCond_FirstUseEver); Begin("Debug##Default"); @@ -4495,110 +4577,6 @@ void ImGui::NewFrame() CallContextHooks(&g, ImGuiContextHookType_NewFramePost); } -void ImGui::Initialize() -{ - ImGuiContext& g = *GImGui; - IM_ASSERT(!g.Initialized && !g.SettingsLoaded); - - // Add .ini handle for ImGuiWindow type - { - ImGuiSettingsHandler ini_handler; - ini_handler.TypeName = "Window"; - ini_handler.TypeHash = ImHashStr("Window"); - ini_handler.ClearAllFn = WindowSettingsHandler_ClearAll; - ini_handler.ReadOpenFn = WindowSettingsHandler_ReadOpen; - ini_handler.ReadLineFn = WindowSettingsHandler_ReadLine; - ini_handler.ApplyAllFn = WindowSettingsHandler_ApplyAll; - ini_handler.WriteAllFn = WindowSettingsHandler_WriteAll; - AddSettingsHandler(&ini_handler); - } - - // Add .ini handle for ImGuiTable type - TableSettingsAddSettingsHandler(); - - // Create default viewport - ImGuiViewportP* viewport = IM_NEW(ImGuiViewportP)(); - g.Viewports.push_back(viewport); - g.TempBuffer.resize(1024 * 3 + 1, 0); - -#ifdef IMGUI_HAS_DOCK -#endif - - g.Initialized = true; -} - -// This function is merely here to free heap allocations. -void ImGui::Shutdown() -{ - // The fonts atlas can be used prior to calling NewFrame(), so we clear it even if g.Initialized is FALSE (which would happen if we never called NewFrame) - ImGuiContext& g = *GImGui; - if (g.IO.Fonts && g.FontAtlasOwnedByContext) - { - g.IO.Fonts->Locked = false; - IM_DELETE(g.IO.Fonts); - } - g.IO.Fonts = NULL; - - // Cleanup of other data are conditional on actually having initialized Dear ImGui. - if (!g.Initialized) - return; - - // Save settings (unless we haven't attempted to load them: CreateContext/DestroyContext without a call to NewFrame shouldn't save an empty file) - if (g.SettingsLoaded && g.IO.IniFilename != NULL) - SaveIniSettingsToDisk(g.IO.IniFilename); - - CallContextHooks(&g, ImGuiContextHookType_Shutdown); - - // Clear everything else - g.Windows.clear_delete(); - g.WindowsFocusOrder.clear(); - g.WindowsTempSortBuffer.clear(); - g.CurrentWindow = NULL; - g.CurrentWindowStack.clear(); - g.WindowsById.Clear(); - g.NavWindow = NULL; - g.HoveredWindow = g.HoveredWindowUnderMovingWindow = NULL; - g.ActiveIdWindow = g.ActiveIdPreviousFrameWindow = NULL; - g.MovingWindow = NULL; - g.ColorStack.clear(); - g.StyleVarStack.clear(); - g.FontStack.clear(); - g.OpenPopupStack.clear(); - g.BeginPopupStack.clear(); - - g.Viewports.clear_delete(); - - g.TabBars.Clear(); - g.CurrentTabBarStack.clear(); - g.ShrinkWidthBuffer.clear(); - - g.ClipperTempData.clear_destruct(); - - g.Tables.Clear(); - g.TablesTempData.clear_destruct(); - g.DrawChannelsTempMergeBuffer.clear(); - - g.ClipboardHandlerData.clear(); - g.MenusIdSubmittedThisFrame.clear(); - g.InputTextState.ClearFreeMemory(); - - g.SettingsWindows.clear(); - g.SettingsHandlers.clear(); - - if (g.LogFile) - { -#ifndef IMGUI_DISABLE_TTY_FUNCTIONS - if (g.LogFile != stdout) -#endif - ImFileClose(g.LogFile); - g.LogFile = NULL; - } - g.LogBuffer.clear(); - g.DebugLogBuf.clear(); - - g.Initialized = false; -} - // FIXME: Add a more explicit sort order in the window structure. static int IMGUI_CDECL ChildWindowComparer(const void* lhs, const void* rhs) { @@ -4900,9 +4878,9 @@ void ImGui::EndFrame() g.IO.Fonts->Locked = false; // Clear Input data for next frame + g.IO.AppFocusLost = false; g.IO.MouseWheel = g.IO.MouseWheelH = 0.0f; g.IO.InputQueueCharacters.resize(0); - memset(g.IO.NavInputs, 0, sizeof(g.IO.NavInputs)); CallContextHooks(&g, ImGuiContextHookType_EndFramePost); } @@ -5139,7 +5117,7 @@ bool ImGui::IsAnyItemFocused() bool ImGui::IsItemVisible() { ImGuiContext& g = *GImGui; - return g.CurrentWindow->ClipRect.Overlaps(g.LastItemData.Rect); + return (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) != 0; } bool ImGui::IsItemEdited() @@ -5160,26 +5138,22 @@ void ImGui::SetItemAllowOverlap() g.ActiveIdAllowOverlap = true; } -void ImGui::SetItemUsingMouseWheel() -{ - ImGuiContext& g = *GImGui; - ImGuiID id = g.LastItemData.ID; - if (g.HoveredId == id) - g.HoveredIdUsingMouseWheel = true; - if (g.ActiveId == id) - g.ActiveIdUsingMouseWheel = true; -} - -void ImGui::SetActiveIdUsingNavAndKeys() +// FIXME: It might be undesirable that this will likely disable KeyOwner-aware shortcuts systems. Consider a more fine-tuned version for the two users of this function. +void ImGui::SetActiveIdUsingAllKeyboardKeys() { ImGuiContext& g = *GImGui; IM_ASSERT(g.ActiveId != 0); - g.ActiveIdUsingNavDirMask = ~(ImU32)0; - g.ActiveIdUsingNavInputMask = ~(ImU32)0; - g.ActiveIdUsingKeyInputMask.SetAllBits(); + g.ActiveIdUsingNavDirMask = (1 << ImGuiDir_COUNT) - 1; + g.ActiveIdUsingAllKeyboardKeys = true; NavMoveRequestCancel(); } +ImGuiID ImGui::GetItemID() +{ + ImGuiContext& g = *GImGui; + return g.LastItemData.ID; +} + ImVec2 ImGui::GetItemRectMin() { ImGuiContext& g = *GImGui; @@ -5211,7 +5185,7 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, b ImVec2 size = ImFloor(size_arg); const int auto_fit_axises = ((size.x == 0.0f) ? (1 << ImGuiAxis_X) : 0x00) | ((size.y == 0.0f) ? (1 << ImGuiAxis_Y) : 0x00); if (size.x <= 0.0f) - size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too much issues) + size.x = ImMax(content_avail.x + size.x, 4.0f); // Arbitrary minimum child size (0.0f causing too many issues) if (size.y <= 0.0f) size.y = ImMax(content_avail.y + size.y, 4.0f); SetNextWindowSize(size); @@ -5358,7 +5332,7 @@ static void UpdateWindowInFocusOrderList(ImGuiWindow* window, bool just_created, { ImGuiContext& g = *GImGui; - const bool new_is_explicit_child = (new_flags & ImGuiWindowFlags_ChildWindow) != 0; + const bool new_is_explicit_child = (new_flags & ImGuiWindowFlags_ChildWindow) != 0 && ((new_flags & ImGuiWindowFlags_Popup) == 0 || (new_flags & ImGuiWindowFlags_ChildMenu) != 0); const bool child_flag_changed = new_is_explicit_child != window->IsExplicitChild; if ((just_created || child_flag_changed) && !new_is_explicit_child) { @@ -5420,7 +5394,6 @@ static ImGuiWindow* CreateNewWindow(const char* name, ImGuiWindowFlags flags) g.Windows.push_front(window); // Quite slow but rare and only once else g.Windows.push_back(window); - UpdateWindowInFocusOrderList(window, true, window->Flags); return window; } @@ -5453,9 +5426,9 @@ static ImVec2 CalcWindowSizeAfterConstraint(ImGuiWindow* window, const ImVec2& s if (!(window->Flags & (ImGuiWindowFlags_ChildWindow | ImGuiWindowFlags_AlwaysAutoResize))) { ImGuiWindow* window_for_height = window; - const float decoration_up_height = window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight(); new_size = ImMax(new_size, g.Style.WindowMinSize); - new_size.y = ImMax(new_size.y, decoration_up_height + ImMax(0.0f, g.Style.WindowRounding - 1.0f)); // Reduce artifacts with very small windows + const float minimum_height = window_for_height->TitleBarHeight() + window_for_height->MenuBarHeight() + ImMax(0.0f, g.Style.WindowRounding - 1.0f); + new_size.y = ImMax(new_size.y, minimum_height); // Reduce artifacts with very small windows } return new_size; } @@ -5484,9 +5457,10 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont { ImGuiContext& g = *GImGui; ImGuiStyle& style = g.Style; - const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); + const float decoration_w_without_scrollbars = window->DecoOuterSizeX1 + window->DecoOuterSizeX2 - window->ScrollbarSizes.x; + const float decoration_h_without_scrollbars = window->DecoOuterSizeY1 + window->DecoOuterSizeY2 - window->ScrollbarSizes.y; ImVec2 size_pad = window->WindowPadding * 2.0f; - ImVec2 size_desired = size_contents + size_pad + ImVec2(0.0f, decoration_up_height); + ImVec2 size_desired = size_contents + size_pad + ImVec2(decoration_w_without_scrollbars, decoration_h_without_scrollbars); if (window->Flags & ImGuiWindowFlags_Tooltip) { // Tooltip always resize @@ -5501,15 +5475,14 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont if (is_popup || is_menu) // Popups and menus bypass style.WindowMinSize by default, but we give then a non-zero minimum size to facilitate understanding problematic cases (e.g. empty popups) size_min = ImMin(size_min, ImVec2(4.0f, 4.0f)); - // FIXME-VIEWPORT-WORKAREA: May want to use GetWorkSize() instead of Size depending on the type of windows? - ImVec2 avail_size = ImGui::GetMainViewport()->Size; + ImVec2 avail_size = ImGui::GetMainViewport()->WorkSize; ImVec2 size_auto_fit = ImClamp(size_desired, size_min, ImMax(size_min, avail_size - style.DisplaySafeAreaPadding * 2.0f)); // When the window cannot fit all contents (either because of constraints, either because screen is too small), // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding. ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit); - bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - 0.0f < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar); - bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - decoration_up_height < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar); + bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - decoration_w_without_scrollbars < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar); + bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - decoration_h_without_scrollbars < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar); if (will_have_scrollbar_x) size_auto_fit.y += style.ScrollbarSize; if (will_have_scrollbar_y) @@ -5616,7 +5589,7 @@ ImGuiID ImGui::GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir) } // Handle resize for: Resize Grips, Borders, Gamepad -// Return true when using auto-fit (double click on resize grip) +// Return true when using auto-fit (double-click on resize grip) static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& size_auto_fit, int* border_held, int resize_grip_count, ImU32 resize_grip_col[4], const ImRect& visibility_rect) { ImGuiContext& g = *GImGui; @@ -5624,7 +5597,7 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s if ((flags & ImGuiWindowFlags_NoResize) || (flags & ImGuiWindowFlags_AlwaysAutoResize) || window->AutoFitFramesX > 0 || window->AutoFitFramesY > 0) return false; - if (window->WasActive == false) // Early out to avoid running this code for e.g. an hidden implicit/fallback Debug window. + if (window->WasActive == false) // Early out to avoid running this code for e.g. a hidden implicit/fallback Debug window. return false; bool ret_auto_fit = false; @@ -5652,7 +5625,7 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s if (resize_rect.Min.x > resize_rect.Max.x) ImSwap(resize_rect.Min.x, resize_rect.Max.x); if (resize_rect.Min.y > resize_rect.Max.y) ImSwap(resize_rect.Min.y, resize_rect.Max.y); ImGuiID resize_grip_id = window->GetID(resize_grip_n); // == GetWindowResizeCornerID() - KeepAliveID(resize_grip_id); + ItemAdd(resize_rect, resize_grip_id, NULL, ImGuiItemFlags_NoNav); ButtonBehavior(resize_rect, resize_grip_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); //GetForegroundDrawList(window)->AddRect(resize_rect.Min, resize_rect.Max, IM_COL32(255, 255, 0, 255)); if (hovered || held) @@ -5688,7 +5661,7 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s bool hovered, held; ImRect border_rect = GetResizeBorderRect(window, border_n, grip_hover_inner_size, WINDOWS_HOVER_PADDING); ImGuiID border_id = window->GetID(border_n + 4); // == GetWindowResizeBorderID() - KeepAliveID(border_id); + ItemAdd(border_rect, border_id, NULL, ImGuiItemFlags_NoNav); ButtonBehavior(border_rect, border_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_NoNavFocus); //GetForegroundDrawLists(window)->AddRect(border_rect.Min, border_rect.Max, IM_COL32(255, 255, 0, 255)); if ((hovered && g.HoveredIdTimer > WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER) || held) @@ -5713,23 +5686,31 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s window->DC.NavLayerCurrent = ImGuiNavLayer_Main; // Navigation resize (keyboard/gamepad) + // FIXME: This cannot be moved to NavUpdateWindowing() because CalcWindowSizeAfterConstraint() need to callback into user. + // Not even sure the callback works here. if (g.NavWindowingTarget && g.NavWindowingTarget->RootWindow == window) { - ImVec2 nav_resize_delta; + ImVec2 nav_resize_dir; if (g.NavInputSource == ImGuiInputSource_Keyboard && g.IO.KeyShift) - nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_RawKeyboard, ImGuiNavReadMode_Down); + nav_resize_dir = GetKeyMagnitude2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow); if (g.NavInputSource == ImGuiInputSource_Gamepad) - nav_resize_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadDPad, ImGuiNavReadMode_Down); - if (nav_resize_delta.x != 0.0f || nav_resize_delta.y != 0.0f) + nav_resize_dir = GetKeyMagnitude2d(ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown); + if (nav_resize_dir.x != 0.0f || nav_resize_dir.y != 0.0f) { const float NAV_RESIZE_SPEED = 600.0f; - nav_resize_delta *= ImFloor(NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y)); - nav_resize_delta = ImMax(nav_resize_delta, visibility_rect.Min - window->Pos - window->Size); + const float resize_step = NAV_RESIZE_SPEED * g.IO.DeltaTime * ImMin(g.IO.DisplayFramebufferScale.x, g.IO.DisplayFramebufferScale.y); + g.NavWindowingAccumDeltaSize += nav_resize_dir * resize_step; + g.NavWindowingAccumDeltaSize = ImMax(g.NavWindowingAccumDeltaSize, visibility_rect.Min - window->Pos - window->Size); // We need Pos+Size >= visibility_rect.Min, so Size >= visibility_rect.Min - Pos, so size_delta >= visibility_rect.Min - window->Pos - window->Size g.NavWindowingToggleLayer = false; g.NavDisableMouseHover = true; resize_grip_col[0] = GetColorU32(ImGuiCol_ResizeGripActive); - // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck. - size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + nav_resize_delta); + ImVec2 accum_floored = ImFloor(g.NavWindowingAccumDeltaSize); + if (accum_floored.x != 0.0f || accum_floored.y != 0.0f) + { + // FIXME-NAV: Should store and accumulate into a separate size buffer to handle sizing constraints properly, right now a constraint will make us stuck. + size_target = CalcWindowSizeAfterConstraint(window, window->SizeFull + accum_floored); + g.NavWindowingAccumDeltaSize -= accum_floored; + } } } @@ -5749,7 +5730,7 @@ static bool ImGui::UpdateWindowManualResize(ImGuiWindow* window, const ImVec2& s return ret_auto_fit; } -static inline void ClampWindowRect(ImGuiWindow* window, const ImRect& visibility_rect) +static inline void ClampWindowPos(ImGuiWindow* window, const ImRect& visibility_rect) { ImGuiContext& g = *GImGui; ImVec2 size_for_clamping = window->Size; @@ -5784,7 +5765,7 @@ static void ImGui::RenderWindowOuterBorders(ImGuiWindow* window) // Draw background and borders // Draw and handle scrollbars -void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size) +void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar_rect, bool title_bar_is_highlight, bool handle_borders_and_resize_grips, int resize_grip_count, const ImU32 resize_grip_col[4], float resize_grip_draw_size) { ImGuiContext& g = *GImGui; ImGuiStyle& style = g.Style; @@ -5795,7 +5776,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar window->SkipItems = false; // Draw window + handle manual resize - // As we highlight the title bar when want_focus is set, multiple reappearing windows will have have their title bar highlighted on their reappearing frame. + // As we highlight the title bar when want_focus is set, multiple reappearing windows will have their title bar highlighted on their reappearing frame. const float window_rounding = window->WindowRounding; const float window_border_size = window->WindowBorderSize; if (window->Collapsed) @@ -5849,7 +5830,7 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar Scrollbar(ImGuiAxis_Y); // Render resize grips (after their input handling so we don't have a frame of latency) - if (!(flags & ImGuiWindowFlags_NoResize)) + if (handle_borders_and_resize_grips && !(flags & ImGuiWindowFlags_NoResize)) { for (int resize_grip_n = 0; resize_grip_n < resize_grip_count; resize_grip_n++) { @@ -5863,7 +5844,8 @@ void ImGui::RenderWindowDecorations(ImGuiWindow* window, const ImRect& title_bar } // Borders - RenderWindowOuterBorders(window); + if (handle_borders_and_resize_grips) + RenderWindowOuterBorders(window); } } @@ -6025,8 +6007,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) const bool window_just_created = (window == NULL); if (window_just_created) window = CreateNewWindow(name, flags); - else - UpdateWindowInFocusOrderList(window, window_just_created, flags); // Automatically disable manual moving/resizing when NoInputs is set if ((flags & ImGuiWindowFlags_NoInputs) == ImGuiWindowFlags_NoInputs) @@ -6054,6 +6034,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Update Flags, LastFrameActive, BeginOrderXXX fields if (first_begin_of_the_frame) { + UpdateWindowInFocusOrderList(window, window_just_created, flags); window->Flags = (ImGuiWindowFlags)flags; window->LastFrameActive = current_frame; window->LastTimeActive = (float)g.Time; @@ -6082,10 +6063,22 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window_stack_data.ParentLastItemDataBackup = g.LastItemData; window_stack_data.StackSizesOnBegin.SetToCurrentState(); g.CurrentWindowStack.push_back(window_stack_data); - g.CurrentWindow = NULL; if (flags & ImGuiWindowFlags_ChildMenu) g.BeginMenuCount++; + // Update ->RootWindow and others pointers (before any possible call to FocusWindow) + if (first_begin_of_the_frame) + { + UpdateWindowParentAndRootLinks(window, flags, parent_window); + window->ParentWindowInBeginStack = parent_window_in_stack; + } + + // Add to focus scope stack + PushFocusScope(window->ID); + window->NavRootFocusScopeId = g.CurrentFocusScopeId; + g.CurrentWindow = NULL; + + // Add to popup stack if (flags & ImGuiWindowFlags_Popup) { ImGuiPopupData& popup_ref = g.OpenPopupStack[g.BeginPopupStack.Size]; @@ -6095,13 +6088,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->PopupId = popup_ref.PopupId; } - // Update ->RootWindow and others pointers (before any possible call to FocusWindow) - if (first_begin_of_the_frame) - { - UpdateWindowParentAndRootLinks(window, flags, parent_window); - window->ParentWindowInBeginStack = parent_window_in_stack; - } - // Process SetNextWindow***() calls // (FIXME: Consider splitting the HasXXX flags into X/Y components bool window_pos_set_by_api = false; @@ -6232,6 +6218,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.MenuBarOffset.x = ImMax(ImMax(window->WindowPadding.x, style.ItemSpacing.x), g.NextWindowData.MenuBarOffsetMinVal.x); window->DC.MenuBarOffset.y = g.NextWindowData.MenuBarOffsetMinVal.y; + bool use_current_size_for_scrollbar_x = window_just_created; + bool use_current_size_for_scrollbar_y = window_just_created; + // Collapse window by double-clicking on title bar // At this point we don't have a clipping rectangle setup yet, so we can use the title bar area for hit detection and drawing if (!(flags & ImGuiWindowFlags_NoTitleBar) && !(flags & ImGuiWindowFlags_NoCollapse)) @@ -6243,6 +6232,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (window->WantCollapseToggle) { window->Collapsed = !window->Collapsed; + if (!window->Collapsed) + use_current_size_for_scrollbar_y = true; MarkIniSettingsDirty(window); } } @@ -6254,10 +6245,17 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // SIZE + // Outer Decoration Sizes + // (we need to clear ScrollbarSize immediatly as CalcWindowAutoFitSize() needs it and can be called from other locations). + const ImVec2 scrollbar_sizes_from_last_frame = window->ScrollbarSizes; + window->DecoOuterSizeX1 = 0.0f; + window->DecoOuterSizeX2 = 0.0f; + window->DecoOuterSizeY1 = window->TitleBarHeight() + window->MenuBarHeight(); + window->DecoOuterSizeY2 = 0.0f; + window->ScrollbarSizes = ImVec2(0.0f, 0.0f); + // Calculate auto-fit size, handle automatic resize const ImVec2 size_auto_fit = CalcWindowAutoFitSize(window, window->ContentSizeIdeal); - bool use_current_size_for_scrollbar_x = window_just_created; - bool use_current_size_for_scrollbar_y = window_just_created; if ((flags & ImGuiWindowFlags_AlwaysAutoResize) && !window->Collapsed) { // Using SetNextWindowSize() overrides ImGuiWindowFlags_AlwaysAutoResize, so it can be used on tooltips/popups, etc. @@ -6294,9 +6292,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->SizeFull = CalcWindowSizeAfterConstraint(window, window->SizeFull); window->Size = window->Collapsed && !(flags & ImGuiWindowFlags_ChildWindow) ? window->TitleBarRect().GetSize() : window->SizeFull; - // Decoration size - const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); - // POSITION // Popup latch its initial position, will position itself when it appears next frame @@ -6336,9 +6331,9 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Clamp position/size so window stays visible within its viewport or monitor // Ignore zero-sized display explicitly to avoid losing positions if a window manager reports zero-sized window when initializing or minimizing. - if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow) && window->AutoFitFramesX <= 0 && window->AutoFitFramesY <= 0) + if (!window_pos_set_by_api && !(flags & ImGuiWindowFlags_ChildWindow)) if (viewport_rect.GetWidth() > 0.0f && viewport_rect.GetHeight() > 0.0f) - ClampWindowRect(window, visibility_rect); + ClampWindowPos(window, visibility_rect); window->Pos = ImFloor(window->Pos); // Lock window rounding for the frame (so that altering them doesn't cause inconsistencies) @@ -6380,7 +6375,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (g.TestEngineHookItems) { IM_ASSERT(window->IDStack.Size == 1); - window->IDStack.Size = 0; + window->IDStack.Size = 0; // As window->IDStack[0] == window->ID here, make sure TestEngine doesn't erroneously see window as parent of itself. IMGUI_TEST_ENGINE_ITEM_ADD(window->Rect(), window->ID); IMGUI_TEST_ENGINE_ITEM_INFO(window->ID, window->Name, (g.HoveredWindow == window) ? ImGuiItemStatusFlags_HoveredRect : 0); window->IDStack.Size = 1; @@ -6403,9 +6398,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (!window->Collapsed) { // When reading the current size we need to read it after size constraints have been applied. - // When we use InnerRect here we are intentionally reading last frame size, same for ScrollbarSizes values before we set them again. - ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - decoration_up_height); - ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + window->ScrollbarSizes; + // Intentionally use previous frame values for InnerRect and ScrollbarSizes. + // And when we use window->DecorationUp here it doesn't have ScrollbarSizes.y applied yet. + ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2)); + ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + scrollbar_sizes_from_last_frame; ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f; float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x; float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y; @@ -6415,10 +6411,14 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) if (window->ScrollbarX && !window->ScrollbarY) window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar); window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f); + + // Amend the partially filled window->DecorationXXX values. + window->DecoOuterSizeX2 += window->ScrollbarSizes.x; + window->DecoOuterSizeY2 += window->ScrollbarSizes.y; } // UPDATE RECTANGLES (1- THOSE NOT AFFECTED BY SCROLLING) - // Update various regions. Variables they depends on should be set above in this function. + // Update various regions. Variables they depend on should be set above in this function. // We set this up after processing the resize grip so that our rectangles doesn't lag by a frame. // Outer rectangle @@ -6438,10 +6438,10 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // - ScrollToRectEx() // - NavUpdatePageUpPageDown() // - Scrollbar() - window->InnerRect.Min.x = window->Pos.x; - window->InnerRect.Min.y = window->Pos.y + decoration_up_height; - window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->ScrollbarSizes.x; - window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->ScrollbarSizes.y; + window->InnerRect.Min.x = window->Pos.x + window->DecoOuterSizeX1; + window->InnerRect.Min.y = window->Pos.y + window->DecoOuterSizeY1; + window->InnerRect.Max.x = window->Pos.x + window->Size.x - window->DecoOuterSizeX2; + window->InnerRect.Max.y = window->Pos.y + window->Size.y - window->DecoOuterSizeY2; // Inner clipping rectangle. // Will extend a little bit outside the normal work region. @@ -6474,6 +6474,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Apply scrolling window->Scroll = CalcNextScrollFromScrollTargetAndClamp(window); window->ScrollTarget = ImVec2(FLT_MAX, FLT_MAX); + window->DecoInnerSizeX1 = window->DecoInnerSizeY1 = 0.0f; // DRAWING @@ -6503,7 +6504,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Handle title bar, scrollbar, resize grips and resize borders const ImGuiWindow* window_to_highlight = g.NavWindowingTarget ? g.NavWindowingTarget : g.NavWindow; const bool title_bar_is_highlight = want_focus || (window_to_highlight && window->RootWindowForTitleBarHighlight == window_to_highlight->RootWindowForTitleBarHighlight); - RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, resize_grip_count, resize_grip_col, resize_grip_draw_size); + const bool handle_borders_and_resize_grips = true; // This exists to facilitate merge with 'docking' branch. + RenderWindowDecorations(window, title_bar_rect, title_bar_is_highlight, handle_borders_and_resize_grips, resize_grip_count, resize_grip_col, resize_grip_draw_size); if (render_decorations_in_parent) window->DrawList = &window->DrawListInst; @@ -6518,8 +6520,8 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // - BeginTabBar() for right-most edge const bool allow_scrollbar_x = !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar); const bool allow_scrollbar_y = !(flags & ImGuiWindowFlags_NoScrollbar); - const float work_rect_size_x = (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : ImMax(allow_scrollbar_x ? window->ContentSize.x : 0.0f, window->Size.x - window->WindowPadding.x * 2.0f - window->ScrollbarSizes.x)); - const float work_rect_size_y = (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : ImMax(allow_scrollbar_y ? window->ContentSize.y : 0.0f, window->Size.y - window->WindowPadding.y * 2.0f - decoration_up_height - window->ScrollbarSizes.y)); + const float work_rect_size_x = (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : ImMax(allow_scrollbar_x ? window->ContentSize.x : 0.0f, window->Size.x - window->WindowPadding.x * 2.0f - (window->DecoOuterSizeX1 + window->DecoOuterSizeX2))); + const float work_rect_size_y = (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : ImMax(allow_scrollbar_y ? window->ContentSize.y : 0.0f, window->Size.y - window->WindowPadding.y * 2.0f - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2))); window->WorkRect.Min.x = ImFloor(window->InnerRect.Min.x - window->Scroll.x + ImMax(window->WindowPadding.x, window->WindowBorderSize)); window->WorkRect.Min.y = ImFloor(window->InnerRect.Min.y - window->Scroll.y + ImMax(window->WindowPadding.y, window->WindowBorderSize)); window->WorkRect.Max.x = window->WorkRect.Min.x + work_rect_size_x; @@ -6530,21 +6532,21 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // FIXME-OBSOLETE: window->ContentRegionRect.Max is currently very misleading / partly faulty, but some BeginChild() patterns relies on it. // Used by: // - Mouse wheel scrolling + many other things - window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x; - window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + decoration_up_height; - window->ContentRegionRect.Max.x = window->ContentRegionRect.Min.x + (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : (window->Size.x - window->WindowPadding.x * 2.0f - window->ScrollbarSizes.x)); - window->ContentRegionRect.Max.y = window->ContentRegionRect.Min.y + (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : (window->Size.y - window->WindowPadding.y * 2.0f - decoration_up_height - window->ScrollbarSizes.y)); + window->ContentRegionRect.Min.x = window->Pos.x - window->Scroll.x + window->WindowPadding.x + window->DecoOuterSizeX1; + window->ContentRegionRect.Min.y = window->Pos.y - window->Scroll.y + window->WindowPadding.y + window->DecoOuterSizeY1; + window->ContentRegionRect.Max.x = window->ContentRegionRect.Min.x + (window->ContentSizeExplicit.x != 0.0f ? window->ContentSizeExplicit.x : (window->Size.x - window->WindowPadding.x * 2.0f - (window->DecoOuterSizeX1 + window->DecoOuterSizeX2))); + window->ContentRegionRect.Max.y = window->ContentRegionRect.Min.y + (window->ContentSizeExplicit.y != 0.0f ? window->ContentSizeExplicit.y : (window->Size.y - window->WindowPadding.y * 2.0f - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2))); // Setup drawing context // (NB: That term "drawing context / DC" lost its meaning a long time ago. Initially was meant to hold transient data only. Nowadays difference between window-> and window->DC-> is dubious.) - window->DC.Indent.x = 0.0f + window->WindowPadding.x - window->Scroll.x; + window->DC.Indent.x = window->DecoOuterSizeX1 + window->WindowPadding.x - window->Scroll.x; window->DC.GroupOffset.x = 0.0f; window->DC.ColumnsOffset.x = 0.0f; // Record the loss of precision of CursorStartPos which can happen due to really large scrolling amount. // This is used by clipper to compensate and fix the most common use case of large scroll area. Easy and cheap, next best thing compared to switching everything to double or ImU64. - double start_pos_highp_x = (double)window->Pos.x + window->WindowPadding.x - (double)window->Scroll.x + window->DC.ColumnsOffset.x; - double start_pos_highp_y = (double)window->Pos.y + window->WindowPadding.y - (double)window->Scroll.y + decoration_up_height; + double start_pos_highp_x = (double)window->Pos.x + window->WindowPadding.x - (double)window->Scroll.x + window->DecoOuterSizeX1 + window->DC.ColumnsOffset.x; + double start_pos_highp_y = (double)window->Pos.y + window->WindowPadding.y - (double)window->Scroll.y + window->DecoOuterSizeY1; window->DC.CursorStartPos = ImVec2((float)start_pos_highp_x, (float)start_pos_highp_y); window->DC.CursorStartPosLossyness = ImVec2((float)(start_pos_highp_x - window->DC.CursorStartPos.x), (float)(start_pos_highp_y - window->DC.CursorStartPos.y)); window->DC.CursorPos = window->DC.CursorStartPos; @@ -6553,7 +6555,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->DC.IdealMaxPos = window->DC.CursorStartPos; window->DC.CurrLineSize = window->DC.PrevLineSize = ImVec2(0.0f, 0.0f); window->DC.CurrLineTextBaseOffset = window->DC.PrevLineTextBaseOffset = 0.0f; - window->DC.IsSameLine = false; + window->DC.IsSameLine = window->DC.IsSetPos = false; window->DC.NavLayerCurrent = ImGuiNavLayer_Main; window->DC.NavLayersActiveMask = window->DC.NavLayersActiveMaskNext; @@ -6601,7 +6603,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) /* //if (g.NavWindow == window && g.ActiveId == 0) if (g.ActiveId == window->MoveId) - if (g.IO.KeyCtrl && IsKeyPressedMap(ImGuiKey_C)) + if (g.IO.KeyCtrl && IsKeyPressed(ImGuiKey_C)) LogToClipboard(); */ @@ -6609,9 +6611,17 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // This is useful to allow creating context menus on title bar only, etc. SetLastItemData(window->MoveId, g.CurrentItemFlags, IsMouseHoveringRect(title_bar_rect.Min, title_bar_rect.Max, false) ? ImGuiItemStatusFlags_HoveredRect : 0, title_bar_rect); + // [DEBUG] +#ifndef IMGUI_DISABLE_DEBUG_TOOLS + if (g.DebugLocateId != 0 && (window->ID == g.DebugLocateId || window->MoveId == g.DebugLocateId)) + DebugLocateItemResolveWithLastItem(); +#endif + // [Test Engine] Register title bar / tab +#ifdef IMGUI_ENABLE_TEST_ENGINE if (!(window->Flags & ImGuiWindowFlags_NoTitleBar)) IMGUI_TEST_ENGINE_ITEM_ADD(g.LastItemData.Rect, g.LastItemData.ID); +#endif } else { @@ -6619,9 +6629,6 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) SetCurrentWindow(window); } - // Pull/inherit current state - window->DC.NavFocusScopeIdCurrent = (flags & ImGuiWindowFlags_ChildWindow) ? parent_window->DC.NavFocusScopeIdCurrent : window->GetID("#FOCUSSCOPE"); // Inherit from parent only // -V595 - PushClipRect(window->InnerClipRect.Min, window->InnerClipRect.Max, true); // Clear 'accessed' flag last thing (After PushClipRect which will set the flag. We want the flag to stay false when the default "Debug" window is unused) @@ -6699,11 +6706,15 @@ void ImGui::End() if (window->DC.CurrentColumns) EndColumns(); PopClipRect(); // Inner window clip rectangle + PopFocusScope(); // Stop logging if (!(window->Flags & ImGuiWindowFlags_ChildWindow)) // FIXME: add more options for scope of logging LogFinish(); + if (window->DC.IsSetPos) + ErrorCheckUsingSetCursorPosToExtendParentBoundaries(); + // Pop from window stack g.LastItemData = g.CurrentWindowStack.back().ParentLastItemDataBackup; if (window->Flags & ImGuiWindowFlags_ChildMenu) @@ -6805,12 +6816,12 @@ void ImGui::FocusWindow(ImGuiWindow* window) g.NavMousePosDirty = true; g.NavId = window ? window->NavLastIds[0] : 0; // Restore NavId g.NavLayer = ImGuiNavLayer_Main; - g.NavFocusScopeId = 0; + g.NavFocusScopeId = window ? window->NavRootFocusScopeId : 0; g.NavIdIsAlive = false; - } - // Close popups if any - ClosePopupsOverWindow(window, false); + // Close popups if any + ClosePopupsOverWindow(window, false); + } // Move the root window to the top of the pile IM_ASSERT(window == NULL || window->RootWindow != NULL); @@ -7376,18 +7387,16 @@ void ImGui::ActivateItem(ImGuiID id) void ImGui::PushFocusScope(ImGuiID id) { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; - g.FocusScopeStack.push_back(window->DC.NavFocusScopeIdCurrent); - window->DC.NavFocusScopeIdCurrent = id; + g.FocusScopeStack.push_back(id); + g.CurrentFocusScopeId = id; } void ImGui::PopFocusScope() { ImGuiContext& g = *GImGui; - ImGuiWindow* window = g.CurrentWindow; IM_ASSERT(g.FocusScopeStack.Size > 0); // Too many PopFocusScope() ? - window->DC.NavFocusScopeIdCurrent = g.FocusScopeStack.back(); g.FocusScopeStack.pop_back(); + g.CurrentFocusScopeId = g.FocusScopeStack.Size ? g.FocusScopeStack.back() : 0; } // Note: this will likely be called ActivateItem() once we rework our Focus/Activation system! @@ -7437,7 +7446,7 @@ void ImGui::SetItemDefaultFocus() g.NavInitResultRectRel = WindowRectAbsToRel(window, g.LastItemData.Rect); NavUpdateAnyRequestFlag(); - // Scroll could be done in NavInitRequestApplyResult() via a opt-in flag (we however don't want regular init requests to scroll) + // Scroll could be done in NavInitRequestApplyResult() via an opt-in flag (we however don't want regular init requests to scroll) if (!IsItemVisible()) ScrollToRectEx(window, g.LastItemData.Rect, ImGuiScrollFlags_None); } @@ -7549,29 +7558,74 @@ bool ImGui::IsRectVisible(const ImVec2& rect_min, const ImVec2& rect_max) //----------------------------------------------------------------------------- // [SECTION] INPUTS //----------------------------------------------------------------------------- - -// Test if mouse cursor is hovering given rectangle -// NB- Rectangle is clipped by our current clip setting -// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding) -bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip) -{ - ImGuiContext& g = *GImGui; - - // Clip - ImRect rect_clipped(r_min, r_max); - if (clip) - rect_clipped.ClipWith(g.CurrentWindow->ClipRect); - - // Expand for touch input - const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding); - if (!rect_for_touch.Contains(g.IO.MousePos)) - return false; - return true; -} +// - GetKeyData() [Internal] +// - GetKeyIndex() [Internal] +// - GetKeyName() +// - GetKeyChordName() [Internal] +// - CalcTypematicRepeatAmount() [Internal] +// - GetTypematicRepeatRate() [Internal] +// - GetKeyPressedAmount() [Internal] +// - GetKeyMagnitude2d() [Internal] +//----------------------------------------------------------------------------- +// - UpdateKeyRoutingTable() [Internal] +// - GetRoutingIdFromOwnerId() [Internal] +// - GetShortcutRoutingData() [Internal] +// - CalcRoutingScore() [Internal] +// - SetShortcutRouting() [Internal] +// - TestShortcutRouting() [Internal] +//----------------------------------------------------------------------------- +// - IsKeyDown() +// - IsKeyPressed() +// - IsKeyReleased() +//----------------------------------------------------------------------------- +// - IsMouseDown() +// - IsMouseClicked() +// - IsMouseReleased() +// - IsMouseDoubleClicked() +// - GetMouseClickedCount() +// - IsMouseHoveringRect() [Internal] +// - IsMouseDragPastThreshold() [Internal] +// - IsMouseDragging() +// - GetMousePos() +// - GetMousePosOnOpeningCurrentPopup() +// - IsMousePosValid() +// - IsAnyMouseDown() +// - GetMouseDragDelta() +// - ResetMouseDragDelta() +// - GetMouseCursor() +// - SetMouseCursor() +//----------------------------------------------------------------------------- +// - UpdateAliasKey() +// - GetMergedModsFromKeys() +// - UpdateKeyboardInputs() +// - UpdateMouseInputs() +//----------------------------------------------------------------------------- +// - LockWheelingWindow [Internal] +// - FindBestWheelingWindow [Internal] +// - UpdateMouseWheel() [Internal] +//----------------------------------------------------------------------------- +// - SetNextFrameWantCaptureKeyboard() +// - SetNextFrameWantCaptureMouse() +//----------------------------------------------------------------------------- +// - GetInputSourceName() [Internal] +// - DebugPrintInputEvent() [Internal] +// - UpdateInputEvents() [Internal] +//----------------------------------------------------------------------------- +// - GetKeyOwner() [Internal] +// - TestKeyOwner() [Internal] +// - SetKeyOwner() [Internal] +// - SetItemKeyOwner() [Internal] +// - Shortcut() [Internal] +//----------------------------------------------------------------------------- ImGuiKeyData* ImGui::GetKeyData(ImGuiKey key) { ImGuiContext& g = *GImGui; + + // Special storage location for mods + if (key & ImGuiMod_Mask_) + key = ConvertSingleModFlagToKey(key); + int index; #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO IM_ASSERT(key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_NamedKey_END); @@ -7587,12 +7641,12 @@ ImGuiKeyData* ImGui::GetKeyData(ImGuiKey key) } #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO -int ImGui::GetKeyIndex(ImGuiKey key) +ImGuiKey ImGui::GetKeyIndex(ImGuiKey key) { ImGuiContext& g = *GImGui; IM_ASSERT(IsNamedKey(key)); const ImGuiKeyData* key_data = GetKeyData(key); - return (int)(key_data - g.IO.KeysData); + return (ImGuiKey)(key_data - g.IO.KeysData); } #endif @@ -7610,12 +7664,14 @@ static const char* const GKeyNames[] = "Pause", "Keypad0", "Keypad1", "Keypad2", "Keypad3", "Keypad4", "Keypad5", "Keypad6", "Keypad7", "Keypad8", "Keypad9", "KeypadDecimal", "KeypadDivide", "KeypadMultiply", "KeypadSubtract", "KeypadAdd", "KeypadEnter", "KeypadEqual", - "GamepadStart", "GamepadBack", "GamepadFaceUp", "GamepadFaceDown", "GamepadFaceLeft", "GamepadFaceRight", - "GamepadDpadUp", "GamepadDpadDown", "GamepadDpadLeft", "GamepadDpadRight", + "GamepadStart", "GamepadBack", + "GamepadFaceLeft", "GamepadFaceRight", "GamepadFaceUp", "GamepadFaceDown", + "GamepadDpadLeft", "GamepadDpadRight", "GamepadDpadUp", "GamepadDpadDown", "GamepadL1", "GamepadR1", "GamepadL2", "GamepadR2", "GamepadL3", "GamepadR3", - "GamepadLStickUp", "GamepadLStickDown", "GamepadLStickLeft", "GamepadLStickRight", - "GamepadRStickUp", "GamepadRStickDown", "GamepadRStickLeft", "GamepadRStickRight", - "ModCtrl", "ModShift", "ModAlt", "ModSuper" + "GamepadLStickLeft", "GamepadLStickRight", "GamepadLStickUp", "GamepadLStickDown", + "GamepadRStickLeft", "GamepadRStickRight", "GamepadRStickUp", "GamepadRStickDown", + "MouseLeft", "MouseRight", "MouseMiddle", "MouseX1", "MouseX2", "MouseWheelX", "MouseWheelY", + "ModCtrl", "ModShift", "ModAlt", "ModSuper", // ReservedForModXXX are showing the ModXXX names. }; IM_STATIC_ASSERT(ImGuiKey_NamedKey_COUNT == IM_ARRAYSIZE(GKeyNames)); @@ -7635,12 +7691,28 @@ const char* ImGui::GetKeyName(ImGuiKey key) #endif if (key == ImGuiKey_None) return "None"; + if (key & ImGuiMod_Mask_) + key = ConvertSingleModFlagToKey(key); if (!IsNamedKey(key)) return "Unknown"; return GKeyNames[key - ImGuiKey_NamedKey_BEGIN]; } +// ImGuiMod_Shortcut is translated to either Ctrl or Super. +void ImGui::GetKeyChordName(ImGuiKeyChord key_chord, char* out_buf, int out_buf_size) +{ + ImGuiContext& g = *GImGui; + if (key_chord & ImGuiMod_Shortcut) + key_chord = ConvertShortcutMod(key_chord); + ImFormatString(out_buf, (size_t)out_buf_size, "%s%s%s%s%s", + (key_chord & ImGuiMod_Ctrl) ? "Ctrl+" : "", + (key_chord & ImGuiMod_Shift) ? "Shift+" : "", + (key_chord & ImGuiMod_Alt) ? "Alt+" : "", + (key_chord & ImGuiMod_Super) ? (g.IO.ConfigMacOSXBehaviors ? "Cmd+" : "Super+") : "", + GetKeyName((ImGuiKey)(key_chord & ~ImGuiMod_Mask_))); +} + // t0 = previous time (e.g.: g.Time - g.IO.DeltaTime) // t1 = current time (e.g.: g.Time) // An event is triggered at: @@ -7659,42 +7731,276 @@ int ImGui::CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, flo return count; } +void ImGui::GetTypematicRepeatRate(ImGuiInputFlags flags, float* repeat_delay, float* repeat_rate) +{ + ImGuiContext& g = *GImGui; + switch (flags & ImGuiInputFlags_RepeatRateMask_) + { + case ImGuiInputFlags_RepeatRateNavMove: *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.80f; return; + case ImGuiInputFlags_RepeatRateNavTweak: *repeat_delay = g.IO.KeyRepeatDelay * 0.72f; *repeat_rate = g.IO.KeyRepeatRate * 0.30f; return; + case ImGuiInputFlags_RepeatRateDefault: default: *repeat_delay = g.IO.KeyRepeatDelay * 1.00f; *repeat_rate = g.IO.KeyRepeatRate * 1.00f; return; + } +} + +// Return value representing the number of presses in the last time period, for the given repeat rate +// (most often returns 0 or 1. The result is generally only >1 when RepeatRate is smaller than DeltaTime, aka large DeltaTime or fast RepeatRate) int ImGui::GetKeyPressedAmount(ImGuiKey key, float repeat_delay, float repeat_rate) { ImGuiContext& g = *GImGui; const ImGuiKeyData* key_data = GetKeyData(key); + if (!key_data->Down) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership) + return 0; const float t = key_data->DownDuration; return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, repeat_delay, repeat_rate); } +// Return 2D vector representing the combination of four cardinal direction, with analog value support (for e.g. ImGuiKey_GamepadLStick* values). +ImVec2 ImGui::GetKeyMagnitude2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down) +{ + return ImVec2( + GetKeyData(key_right)->AnalogValue - GetKeyData(key_left)->AnalogValue, + GetKeyData(key_down)->AnalogValue - GetKeyData(key_up)->AnalogValue); +} + +// Rewrite routing data buffers to strip old entries + sort by key to make queries not touch scattered data. +// Entries D,A,B,B,A,C,B --> A,A,B,B,B,C,D +// Index A:1 B:2 C:5 D:0 --> A:0 B:2 C:5 D:6 +// See 'Metrics->Key Owners & Shortcut Routing' to visualize the result of that operation. +static void ImGui::UpdateKeyRoutingTable(ImGuiKeyRoutingTable* rt) +{ + ImGuiContext& g = *GImGui; + rt->EntriesNext.resize(0); + for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) + { + const int new_routing_start_idx = rt->EntriesNext.Size; + ImGuiKeyRoutingData* routing_entry; + for (int old_routing_idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; old_routing_idx != -1; old_routing_idx = routing_entry->NextEntryIndex) + { + routing_entry = &rt->Entries[old_routing_idx]; + routing_entry->RoutingCurr = routing_entry->RoutingNext; // Update entry + routing_entry->RoutingNext = ImGuiKeyOwner_None; + routing_entry->RoutingNextScore = 255; + if (routing_entry->RoutingCurr == ImGuiKeyOwner_None) + continue; + rt->EntriesNext.push_back(*routing_entry); // Write alive ones into new buffer + + // Apply routing to owner if there's no owner already (RoutingCurr == None at this point) + if (routing_entry->Mods == g.IO.KeyMods) + { + ImGuiKeyOwnerData* owner_data = ImGui::GetKeyOwnerData(key); + if (owner_data->OwnerCurr == ImGuiKeyOwner_None) + owner_data->OwnerCurr = routing_entry->RoutingCurr; + } + } + + // Rewrite linked-list + rt->Index[key - ImGuiKey_NamedKey_BEGIN] = (ImGuiKeyRoutingIndex)(new_routing_start_idx < rt->EntriesNext.Size ? new_routing_start_idx : -1); + for (int n = new_routing_start_idx; n < rt->EntriesNext.Size; n++) + rt->EntriesNext[n].NextEntryIndex = (ImGuiKeyRoutingIndex)((n + 1 < rt->EntriesNext.Size) ? n + 1 : -1); + } + rt->Entries.swap(rt->EntriesNext); // Swap new and old indexes +} + +// owner_id may be None/Any, but routing_id needs to be always be set, so we default to GetCurrentFocusScope(). +static inline ImGuiID GetRoutingIdFromOwnerId(ImGuiID owner_id) +{ + ImGuiContext& g = *GImGui; + return (owner_id != ImGuiKeyOwner_None && owner_id != ImGuiKeyOwner_Any) ? owner_id : g.CurrentFocusScopeId; +} + +ImGuiKeyRoutingData* ImGui::GetShortcutRoutingData(ImGuiKeyChord key_chord) +{ + // Majority of shortcuts will be Key + any number of Mods + // We accept _Single_ mod with ImGuiKey_None. + // - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl); // Legal + // - Shortcut(ImGuiKey_S | ImGuiMod_Ctrl | ImGuiMod_Shift); // Legal + // - Shortcut(ImGuiMod_Ctrl); // Legal + // - Shortcut(ImGuiMod_Ctrl | ImGuiMod_Shift); // Not legal + ImGuiContext& g = *GImGui; + ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable; + ImGuiKeyRoutingData* routing_data; + if (key_chord & ImGuiMod_Shortcut) + key_chord = ConvertShortcutMod(key_chord); + ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); + ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_); + if (key == ImGuiKey_None) + key = ConvertSingleModFlagToKey(mods); + IM_ASSERT(IsNamedKey(key)); + + // Get (in the majority of case, the linked list will have one element so this should be 2 reads. + // Subsequent elements will be contiguous in memory as list is sorted/rebuilt in NewFrame). + for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; idx = routing_data->NextEntryIndex) + { + routing_data = &rt->Entries[idx]; + if (routing_data->Mods == mods) + return routing_data; + } + + // Add to linked-list + ImGuiKeyRoutingIndex routing_data_idx = (ImGuiKeyRoutingIndex)rt->Entries.Size; + rt->Entries.push_back(ImGuiKeyRoutingData()); + routing_data = &rt->Entries[routing_data_idx]; + routing_data->Mods = (ImU16)mods; + routing_data->NextEntryIndex = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; // Setup linked list + rt->Index[key - ImGuiKey_NamedKey_BEGIN] = routing_data_idx; + return routing_data; +} + +// Current score encoding (lower is highest priority): +// - 0: ImGuiInputFlags_RouteGlobalHigh +// - 1: ImGuiInputFlags_RouteFocused (if item active) +// - 2: ImGuiInputFlags_RouteGlobal +// - 3+: ImGuiInputFlags_RouteFocused (if window in focus-stack) +// - 254: ImGuiInputFlags_RouteGlobalLow +// - 255: never route +// 'flags' should include an explicit routing policy +static int CalcRoutingScore(ImGuiWindow* location, ImGuiID owner_id, ImGuiInputFlags flags) +{ + if (flags & ImGuiInputFlags_RouteFocused) + { + ImGuiContext& g = *GImGui; + ImGuiWindow* focused = g.NavWindow; + + // ActiveID gets top priority + // (we don't check g.ActiveIdUsingAllKeys here. Routing is applied but if input ownership is tested later it may discard it) + if (owner_id != 0 && g.ActiveId == owner_id) + return 1; + + // Score based on distance to focused window (lower is better) + // Assuming both windows are submitting a routing request, + // - When Window....... is focused -> Window scores 3 (best), Window/ChildB scores 255 (no match) + // - When Window/ChildB is focused -> Window scores 4, Window/ChildB scores 3 (best) + // Assuming only WindowA is submitting a routing request, + // - When Window/ChildB is focused -> Window scores 4 (best), Window/ChildB doesn't have a score. + if (focused != NULL && focused->RootWindow == location->RootWindow) + for (int next_score = 3; focused != NULL; next_score++) + { + if (focused == location) + { + IM_ASSERT(next_score < 255); + return next_score; + } + focused = (focused->RootWindow != focused) ? focused->ParentWindow : NULL; // FIXME: This could be later abstracted as a focus path + } + return 255; + } + + // ImGuiInputFlags_RouteGlobalHigh is default, so calls without flags are not conditional + if (flags & ImGuiInputFlags_RouteGlobal) + return 2; + if (flags & ImGuiInputFlags_RouteGlobalLow) + return 254; + return 0; +} + +// Request a desired route for an input chord (key + mods). +// Return true if the route is available this frame. +// - Routes and key ownership are attributed at the beginning of next frame based on best score and mod state. +// (Conceptually this does a "Submit for next frame" + "Test for current frame". +// As such, it could be called TrySetXXX or SubmitXXX, or the Submit and Test operations should be separate.) +// - Using 'owner_id == ImGuiKeyOwner_Any/0': auto-assign an owner based on current focus scope (each window has its focus scope by default) +// - Using 'owner_id == ImGuiKeyOwner_None': allows disabling/locking a shortcut. +bool ImGui::SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags) +{ + ImGuiContext& g = *GImGui; + if ((flags & ImGuiInputFlags_RouteMask_) == 0) + flags |= ImGuiInputFlags_RouteGlobalHigh; // IMPORTANT: This is the default for SetShortcutRouting() but NOT Shortcut() + else + IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiInputFlags_RouteMask_)); // Check that only 1 routing flag is used + + if (flags & ImGuiInputFlags_RouteUnlessBgFocused) + if (g.NavWindow == NULL) + return false; + if (flags & ImGuiInputFlags_RouteAlways) + return true; + + const int score = CalcRoutingScore(g.CurrentWindow, owner_id, flags); + if (score == 255) + return false; + + // Submit routing for NEXT frame (assuming score is sufficient) + // FIXME: Could expose a way to use a "serve last" policy for same score resolution (using <= instead of <). + ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord); + const ImGuiID routing_id = GetRoutingIdFromOwnerId(owner_id); + //const bool set_route = (flags & ImGuiInputFlags_ServeLast) ? (score <= routing_data->RoutingNextScore) : (score < routing_data->RoutingNextScore); + if (score < routing_data->RoutingNextScore) + { + routing_data->RoutingNext = routing_id; + routing_data->RoutingNextScore = (ImU8)score; + } + + // Return routing state for CURRENT frame + return routing_data->RoutingCurr == routing_id; +} + +// Currently unused by core (but used by tests) +// Note: this cannot be turned into GetShortcutRouting() because we do the owner_id->routing_id translation, name would be more misleading. +bool ImGui::TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id) +{ + const ImGuiID routing_id = GetRoutingIdFromOwnerId(owner_id); + ImGuiKeyRoutingData* routing_data = GetShortcutRoutingData(key_chord); // FIXME: Could avoid creating entry. + return routing_data->RoutingCurr == routing_id; +} + // Note that Dear ImGui doesn't know the meaning/semantic of ImGuiKey from 0..511: they are legacy native keycodes. // Consider transitioning from 'IsKeyDown(MY_ENGINE_KEY_A)' (<1.87) to IsKeyDown(ImGuiKey_A) (>= 1.87) bool ImGui::IsKeyDown(ImGuiKey key) +{ + return IsKeyDown(key, ImGuiKeyOwner_Any); +} + +bool ImGui::IsKeyDown(ImGuiKey key, ImGuiID owner_id) { const ImGuiKeyData* key_data = GetKeyData(key); if (!key_data->Down) return false; + if (!TestKeyOwner(key, owner_id)) + return false; return true; } bool ImGui::IsKeyPressed(ImGuiKey key, bool repeat) { - ImGuiContext& g = *GImGui; + return IsKeyPressed(key, ImGuiKeyOwner_Any, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None); +} + +// Important: unless legacy IsKeyPressed(ImGuiKey, bool repeat=true) which DEFAULT to repeat, this requires EXPLICIT repeat. +bool ImGui::IsKeyPressed(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags) +{ const ImGuiKeyData* key_data = GetKeyData(key); + if (!key_data->Down) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership) + return false; const float t = key_data->DownDuration; if (t < 0.0f) return false; - const bool pressed = (t == 0.0f) || (repeat && t > g.IO.KeyRepeatDelay && GetKeyPressedAmount(key, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0); + IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsKeyPressed) == 0); // Passing flags not supported by this function! + + bool pressed = (t == 0.0f); + if (!pressed && ((flags & ImGuiInputFlags_Repeat) != 0)) + { + float repeat_delay, repeat_rate; + GetTypematicRepeatRate(flags, &repeat_delay, &repeat_rate); + pressed = (t > repeat_delay) && GetKeyPressedAmount(key, repeat_delay, repeat_rate) > 0; + } if (!pressed) return false; + if (!TestKeyOwner(key, owner_id)) + return false; return true; } bool ImGui::IsKeyReleased(ImGuiKey key) +{ + return IsKeyReleased(key, ImGuiKeyOwner_Any); +} + +bool ImGui::IsKeyReleased(ImGuiKey key, ImGuiID owner_id) { const ImGuiKeyData* key_data = GetKeyData(key); if (key_data->DownDurationPrev < 0.0f || key_data->Down) return false; + if (!TestKeyOwner(key, owner_id)) + return false; return true; } @@ -7702,33 +8008,62 @@ bool ImGui::IsMouseDown(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - return g.IO.MouseDown[button]; + return g.IO.MouseDown[button] && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); // should be same as IsKeyDown(MouseButtonToKey(button), ImGuiKeyOwner_Any), but this allows legacy code hijacking the io.Mousedown[] array. +} + +bool ImGui::IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + return g.IO.MouseDown[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyDown(MouseButtonToKey(button), owner_id), but this allows legacy code hijacking the io.Mousedown[] array. } bool ImGui::IsMouseClicked(ImGuiMouseButton button, bool repeat) +{ + return IsMouseClicked(button, ImGuiKeyOwner_Any, repeat ? ImGuiInputFlags_Repeat : ImGuiInputFlags_None); +} + +bool ImGui::IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInputFlags flags) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + if (!g.IO.MouseDown[button]) // In theory this should already be encoded as (DownDuration < 0.0f), but testing this facilitates eating mechanism (until we finish work on key ownership) + return false; const float t = g.IO.MouseDownDuration[button]; - if (t == 0.0f) - return true; - if (repeat && t > g.IO.KeyRepeatDelay) - return CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0; - return false; + if (t < 0.0f) + return false; + IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByIsKeyPressed) == 0); // Passing flags not supported by this function! + + const bool repeat = (flags & ImGuiInputFlags_Repeat) != 0; + const bool pressed = (t == 0.0f) || (repeat && t > g.IO.KeyRepeatDelay && CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0); + if (!pressed) + return false; + + if (!TestKeyOwner(MouseButtonToKey(button), owner_id)) + return false; + + return true; } bool ImGui::IsMouseReleased(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - return g.IO.MouseReleased[button]; + return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); // Should be same as IsKeyReleased(MouseButtonToKey(button), ImGuiKeyOwner_Any) +} + +bool ImGui::IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id) +{ + ImGuiContext& g = *GImGui; + IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); + return g.IO.MouseReleased[button] && TestKeyOwner(MouseButtonToKey(button), owner_id); // Should be same as IsKeyReleased(MouseButtonToKey(button), owner_id) } bool ImGui::IsMouseDoubleClicked(ImGuiMouseButton button) { ImGuiContext& g = *GImGui; IM_ASSERT(button >= 0 && button < IM_ARRAYSIZE(g.IO.MouseDown)); - return g.IO.MouseClickedCount[button] == 2; + return g.IO.MouseClickedCount[button] == 2 && TestKeyOwner(MouseButtonToKey(button), ImGuiKeyOwner_Any); } int ImGui::GetMouseClickedCount(ImGuiMouseButton button) @@ -7738,6 +8073,25 @@ int ImGui::GetMouseClickedCount(ImGuiMouseButton button) return g.IO.MouseClickedCount[button]; } +// Test if mouse cursor is hovering given rectangle +// NB- Rectangle is clipped by our current clip setting +// NB- Expand the rectangle to be generous on imprecise inputs systems (g.Style.TouchExtraPadding) +bool ImGui::IsMouseHoveringRect(const ImVec2& r_min, const ImVec2& r_max, bool clip) +{ + ImGuiContext& g = *GImGui; + + // Clip + ImRect rect_clipped(r_min, r_max); + if (clip) + rect_clipped.ClipWith(g.CurrentWindow->ClipRect); + + // Expand for touch input + const ImRect rect_for_touch(rect_clipped.Min - g.Style.TouchExtraPadding, rect_clipped.Max + g.Style.TouchExtraPadding); + if (!rect_for_touch.Contains(g.IO.MousePos)) + return false; + return true; +} + // Return if a mouse click/drag went past the given threshold. Valid to call during the MouseReleased frame. // [Internal] This doesn't test if the button is pressed bool ImGui::IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold) @@ -7818,6 +8172,10 @@ void ImGui::ResetMouseDragDelta(ImGuiMouseButton button) g.IO.MouseClickedPos[button] = g.IO.MousePos; } +// Get desired mouse cursor shape. +// Important: this is meant to be used by a platform backend, it is reset in ImGui::NewFrame(), +// updated during the frame, and locked in EndFrame()/Render(). +// If you use software rendering by setting io.MouseDrawCursor then Dear ImGui will render those for you ImGuiMouseCursor ImGui::GetMouseCursor() { ImGuiContext& g = *GImGui; @@ -7830,6 +8188,354 @@ void ImGui::SetMouseCursor(ImGuiMouseCursor cursor_type) g.MouseCursor = cursor_type; } +static void UpdateAliasKey(ImGuiKey key, bool v, float analog_value) +{ + IM_ASSERT(ImGui::IsAliasKey(key)); + ImGuiKeyData* key_data = ImGui::GetKeyData(key); + key_data->Down = v; + key_data->AnalogValue = analog_value; +} + +// [Internal] Do not use directly +static ImGuiKeyChord GetMergedModsFromKeys() +{ + ImGuiKeyChord mods = 0; + if (ImGui::IsKeyDown(ImGuiMod_Ctrl)) { mods |= ImGuiMod_Ctrl; } + if (ImGui::IsKeyDown(ImGuiMod_Shift)) { mods |= ImGuiMod_Shift; } + if (ImGui::IsKeyDown(ImGuiMod_Alt)) { mods |= ImGuiMod_Alt; } + if (ImGui::IsKeyDown(ImGuiMod_Super)) { mods |= ImGuiMod_Super; } + return mods; +} + +static void ImGui::UpdateKeyboardInputs() +{ + ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; + + // Import legacy keys or verify they are not used +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + if (io.BackendUsingLegacyKeyArrays == 0) + { + // Backend used new io.AddKeyEvent() API: Good! Verify that old arrays are never written to externally. + for (int n = 0; n < ImGuiKey_LegacyNativeKey_END; n++) + IM_ASSERT((io.KeysDown[n] == false || IsKeyDown((ImGuiKey)n)) && "Backend needs to either only use io.AddKeyEvent(), either only fill legacy io.KeysDown[] + io.KeyMap[]. Not both!"); + } + else + { + if (g.FrameCount == 0) + for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++) + IM_ASSERT(g.IO.KeyMap[n] == -1 && "Backend is not allowed to write to io.KeyMap[0..511]!"); + + // Build reverse KeyMap (Named -> Legacy) + for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++) + if (io.KeyMap[n] != -1) + { + IM_ASSERT(IsLegacyKey((ImGuiKey)io.KeyMap[n])); + io.KeyMap[io.KeyMap[n]] = n; + } + + // Import legacy keys into new ones + for (int n = ImGuiKey_LegacyNativeKey_BEGIN; n < ImGuiKey_LegacyNativeKey_END; n++) + if (io.KeysDown[n] || io.BackendUsingLegacyKeyArrays == 1) + { + const ImGuiKey key = (ImGuiKey)(io.KeyMap[n] != -1 ? io.KeyMap[n] : n); + IM_ASSERT(io.KeyMap[n] == -1 || IsNamedKey(key)); + io.KeysData[key].Down = io.KeysDown[n]; + if (key != n) + io.KeysDown[key] = io.KeysDown[n]; // Allow legacy code using io.KeysDown[GetKeyIndex()] with old backends + io.BackendUsingLegacyKeyArrays = 1; + } + if (io.BackendUsingLegacyKeyArrays == 1) + { + GetKeyData(ImGuiMod_Ctrl)->Down = io.KeyCtrl; + GetKeyData(ImGuiMod_Shift)->Down = io.KeyShift; + GetKeyData(ImGuiMod_Alt)->Down = io.KeyAlt; + GetKeyData(ImGuiMod_Super)->Down = io.KeySuper; + } + } + +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; + if (io.BackendUsingLegacyNavInputArray && nav_gamepad_active) + { + #define MAP_LEGACY_NAV_INPUT_TO_KEY1(_KEY, _NAV1) do { io.KeysData[_KEY].Down = (io.NavInputs[_NAV1] > 0.0f); io.KeysData[_KEY].AnalogValue = io.NavInputs[_NAV1]; } while (0) + #define MAP_LEGACY_NAV_INPUT_TO_KEY2(_KEY, _NAV1, _NAV2) do { io.KeysData[_KEY].Down = (io.NavInputs[_NAV1] > 0.0f) || (io.NavInputs[_NAV2] > 0.0f); io.KeysData[_KEY].AnalogValue = ImMax(io.NavInputs[_NAV1], io.NavInputs[_NAV2]); } while (0) + MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceDown, ImGuiNavInput_Activate); + MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceRight, ImGuiNavInput_Cancel); + MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceLeft, ImGuiNavInput_Menu); + MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadFaceUp, ImGuiNavInput_Input); + MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadLeft, ImGuiNavInput_DpadLeft); + MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadRight, ImGuiNavInput_DpadRight); + MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadUp, ImGuiNavInput_DpadUp); + MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadDpadDown, ImGuiNavInput_DpadDown); + MAP_LEGACY_NAV_INPUT_TO_KEY2(ImGuiKey_GamepadL1, ImGuiNavInput_FocusPrev, ImGuiNavInput_TweakSlow); + MAP_LEGACY_NAV_INPUT_TO_KEY2(ImGuiKey_GamepadR1, ImGuiNavInput_FocusNext, ImGuiNavInput_TweakFast); + MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickLeft, ImGuiNavInput_LStickLeft); + MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickRight, ImGuiNavInput_LStickRight); + MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickUp, ImGuiNavInput_LStickUp); + MAP_LEGACY_NAV_INPUT_TO_KEY1(ImGuiKey_GamepadLStickDown, ImGuiNavInput_LStickDown); + #undef NAV_MAP_KEY + } +#endif +#endif + + // Update aliases + for (int n = 0; n < ImGuiMouseButton_COUNT; n++) + UpdateAliasKey(MouseButtonToKey(n), io.MouseDown[n], io.MouseDown[n] ? 1.0f : 0.0f); + UpdateAliasKey(ImGuiKey_MouseWheelX, io.MouseWheelH != 0.0f, io.MouseWheelH); + UpdateAliasKey(ImGuiKey_MouseWheelY, io.MouseWheel != 0.0f, io.MouseWheel); + + // Synchronize io.KeyMods and io.KeyXXX values. + // - New backends (1.87+): send io.AddKeyEvent(ImGuiMod_XXX) -> -> (here) deriving io.KeyMods + io.KeyXXX from key array. + // - Legacy backends: set io.KeyXXX bools -> (above) set key array from io.KeyXXX -> (here) deriving io.KeyMods + io.KeyXXX from key array. + // So with legacy backends the 4 values will do a unnecessary back-and-forth but it makes the code simpler and future facing. + io.KeyMods = GetMergedModsFromKeys(); + io.KeyCtrl = (io.KeyMods & ImGuiMod_Ctrl) != 0; + io.KeyShift = (io.KeyMods & ImGuiMod_Shift) != 0; + io.KeyAlt = (io.KeyMods & ImGuiMod_Alt) != 0; + io.KeySuper = (io.KeyMods & ImGuiMod_Super) != 0; + + // Clear gamepad data if disabled + if ((io.BackendFlags & ImGuiBackendFlags_HasGamepad) == 0) + for (int i = ImGuiKey_Gamepad_BEGIN; i < ImGuiKey_Gamepad_END; i++) + { + io.KeysData[i - ImGuiKey_KeysData_OFFSET].Down = false; + io.KeysData[i - ImGuiKey_KeysData_OFFSET].AnalogValue = 0.0f; + } + + // Update keys + for (int i = 0; i < ImGuiKey_KeysData_SIZE; i++) + { + ImGuiKeyData* key_data = &io.KeysData[i]; + key_data->DownDurationPrev = key_data->DownDuration; + key_data->DownDuration = key_data->Down ? (key_data->DownDuration < 0.0f ? 0.0f : key_data->DownDuration + io.DeltaTime) : -1.0f; + } + + // Update keys/input owner (named keys only): one entry per key + for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) + { + ImGuiKeyData* key_data = &io.KeysData[key - ImGuiKey_KeysData_OFFSET]; + ImGuiKeyOwnerData* owner_data = &g.KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN]; + owner_data->OwnerCurr = owner_data->OwnerNext; + if (!key_data->Down) // Important: ownership is released on the frame after a release. Ensure a 'MouseDown -> CloseWindow -> MouseUp' chain doesn't lead to someone else seeing the MouseUp. + owner_data->OwnerNext = ImGuiKeyOwner_None; + owner_data->LockThisFrame = owner_data->LockUntilRelease = owner_data->LockUntilRelease && key_data->Down; // Clear LockUntilRelease when key is not Down anymore + } + + UpdateKeyRoutingTable(&g.KeysRoutingTable); +} + +static void ImGui::UpdateMouseInputs() +{ + ImGuiContext& g = *GImGui; + ImGuiIO& io = g.IO; + + // Round mouse position to avoid spreading non-rounded position (e.g. UpdateManualResize doesn't support them well) + if (IsMousePosValid(&io.MousePos)) + io.MousePos = g.MouseLastValidPos = ImFloorSigned(io.MousePos); + + // If mouse just appeared or disappeared (usually denoted by -FLT_MAX components) we cancel out movement in MouseDelta + if (IsMousePosValid(&io.MousePos) && IsMousePosValid(&io.MousePosPrev)) + io.MouseDelta = io.MousePos - io.MousePosPrev; + else + io.MouseDelta = ImVec2(0.0f, 0.0f); + + // If mouse moved we re-enable mouse hovering in case it was disabled by gamepad/keyboard. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true. + if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f) + g.NavDisableMouseHover = false; + + io.MousePosPrev = io.MousePos; + for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) + { + io.MouseClicked[i] = io.MouseDown[i] && io.MouseDownDuration[i] < 0.0f; + io.MouseClickedCount[i] = 0; // Will be filled below + io.MouseReleased[i] = !io.MouseDown[i] && io.MouseDownDuration[i] >= 0.0f; + io.MouseDownDurationPrev[i] = io.MouseDownDuration[i]; + io.MouseDownDuration[i] = io.MouseDown[i] ? (io.MouseDownDuration[i] < 0.0f ? 0.0f : io.MouseDownDuration[i] + io.DeltaTime) : -1.0f; + if (io.MouseClicked[i]) + { + bool is_repeated_click = false; + if ((float)(g.Time - io.MouseClickedTime[i]) < io.MouseDoubleClickTime) + { + ImVec2 delta_from_click_pos = IsMousePosValid(&io.MousePos) ? (io.MousePos - io.MouseClickedPos[i]) : ImVec2(0.0f, 0.0f); + if (ImLengthSqr(delta_from_click_pos) < io.MouseDoubleClickMaxDist * io.MouseDoubleClickMaxDist) + is_repeated_click = true; + } + if (is_repeated_click) + io.MouseClickedLastCount[i]++; + else + io.MouseClickedLastCount[i] = 1; + io.MouseClickedTime[i] = g.Time; + io.MouseClickedPos[i] = io.MousePos; + io.MouseClickedCount[i] = io.MouseClickedLastCount[i]; + io.MouseDragMaxDistanceSqr[i] = 0.0f; + } + else if (io.MouseDown[i]) + { + // Maintain the maximum distance we reaching from the initial click position, which is used with dragging threshold + float delta_sqr_click_pos = IsMousePosValid(&io.MousePos) ? ImLengthSqr(io.MousePos - io.MouseClickedPos[i]) : 0.0f; + io.MouseDragMaxDistanceSqr[i] = ImMax(io.MouseDragMaxDistanceSqr[i], delta_sqr_click_pos); + } + + // We provide io.MouseDoubleClicked[] as a legacy service + io.MouseDoubleClicked[i] = (io.MouseClickedCount[i] == 2); + + // Clicking any mouse button reactivate mouse hovering which may have been deactivated by gamepad/keyboard navigation + if (io.MouseClicked[i]) + g.NavDisableMouseHover = false; + } +} + +static void LockWheelingWindow(ImGuiWindow* window, float wheel_amount) +{ + ImGuiContext& g = *GImGui; + if (window) + g.WheelingWindowReleaseTimer = ImMin(g.WheelingWindowReleaseTimer + ImAbs(wheel_amount) * WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER, WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER); + else + g.WheelingWindowReleaseTimer = 0.0f; + if (g.WheelingWindow == window) + return; + IMGUI_DEBUG_LOG_IO("LockWheelingWindow() \"%s\"\n", window ? window->Name : "NULL"); + g.WheelingWindow = window; + g.WheelingWindowRefMousePos = g.IO.MousePos; + if (window == NULL) + { + g.WheelingWindowStartFrame = -1; + g.WheelingAxisAvg = ImVec2(0.0f, 0.0f); + } +} + +static ImGuiWindow* FindBestWheelingWindow(const ImVec2& wheel) +{ + // For each axis, find window in the hierarchy that may want to use scrolling + ImGuiContext& g = *GImGui; + ImGuiWindow* windows[2] = { NULL, NULL }; + for (int axis = 0; axis < 2; axis++) + if (wheel[axis] != 0.0f) + for (ImGuiWindow* window = windows[axis] = g.HoveredWindow; window->Flags & ImGuiWindowFlags_ChildWindow; window = windows[axis] = window->ParentWindow) + { + // Bubble up into parent window if: + // - a child window doesn't allow any scrolling. + // - a child window has the ImGuiWindowFlags_NoScrollWithMouse flag. + //// - a child window doesn't need scrolling because it is already at the edge for the direction we are going in (FIXME-WIP) + const bool has_scrolling = (window->ScrollMax[axis] != 0.0f); + const bool inputs_disabled = (window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs); + //const bool scrolling_past_limits = (wheel_v < 0.0f) ? (window->Scroll[axis] <= 0.0f) : (window->Scroll[axis] >= window->ScrollMax[axis]); + if (has_scrolling && !inputs_disabled) // && !scrolling_past_limits) + break; // select this window + } + if (windows[0] == NULL && windows[1] == NULL) + return NULL; + + // If there's only one window or only one axis then there's no ambiguity + if (windows[0] == windows[1] || windows[0] == NULL || windows[1] == NULL) + return windows[1] ? windows[1] : windows[0]; + + // If candidate are different windows we need to decide which one to prioritize + // - First frame: only find a winner if one axis is zero. + // - Subsequent frames: only find a winner when one is more than the other. + if (g.WheelingWindowStartFrame == -1) + g.WheelingWindowStartFrame = g.FrameCount; + if ((g.WheelingWindowStartFrame == g.FrameCount && wheel.x != 0.0f && wheel.y != 0.0f) || (g.WheelingAxisAvg.x == g.WheelingAxisAvg.y)) + { + g.WheelingWindowWheelRemainder = wheel; + return NULL; + } + return (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? windows[0] : windows[1]; +} + +// Called by NewFrame() +void ImGui::UpdateMouseWheel() +{ + // Reset the locked window if we move the mouse or after the timer elapses. + // FIXME: Ideally we could refactor to have one timer for "changing window w/ same axis" and a shorter timer for "changing window or axis w/ other axis" (#3795) + ImGuiContext& g = *GImGui; + if (g.WheelingWindow != NULL) + { + g.WheelingWindowReleaseTimer -= g.IO.DeltaTime; + if (IsMousePosValid() && ImLengthSqr(g.IO.MousePos - g.WheelingWindowRefMousePos) > g.IO.MouseDragThreshold * g.IO.MouseDragThreshold) + g.WheelingWindowReleaseTimer = 0.0f; + if (g.WheelingWindowReleaseTimer <= 0.0f) + LockWheelingWindow(NULL, 0.0f); + } + + ImVec2 wheel; + wheel.x = TestKeyOwner(ImGuiKey_MouseWheelX, ImGuiKeyOwner_None) ? g.IO.MouseWheelH : 0.0f; + wheel.y = TestKeyOwner(ImGuiKey_MouseWheelY, ImGuiKeyOwner_None) ? g.IO.MouseWheel : 0.0f; + + //IMGUI_DEBUG_LOG("MouseWheel X:%.3f Y:%.3f\n", wheel_x, wheel_y); + ImGuiWindow* mouse_window = g.WheelingWindow ? g.WheelingWindow : g.HoveredWindow; + if (!mouse_window || mouse_window->Collapsed) + return; + + // Zoom / Scale window + // FIXME-OBSOLETE: This is an old feature, it still works but pretty much nobody is using it and may be best redesigned. + if (wheel.y != 0.0f && g.IO.KeyCtrl && g.IO.FontAllowUserScaling) + { + LockWheelingWindow(mouse_window, wheel.y); + ImGuiWindow* window = mouse_window; + const float new_font_scale = ImClamp(window->FontWindowScale + g.IO.MouseWheel * 0.10f, 0.50f, 2.50f); + const float scale = new_font_scale / window->FontWindowScale; + window->FontWindowScale = new_font_scale; + if (window == window->RootWindow) + { + const ImVec2 offset = window->Size * (1.0f - scale) * (g.IO.MousePos - window->Pos) / window->Size; + SetWindowPos(window, window->Pos + offset, 0); + window->Size = ImFloor(window->Size * scale); + window->SizeFull = ImFloor(window->SizeFull * scale); + } + return; + } + if (g.IO.KeyCtrl) + return; + + // Mouse wheel scrolling + // As a standard behavior holding SHIFT while using Vertical Mouse Wheel triggers Horizontal scroll instead + // (we avoid doing it on OSX as it the OS input layer handles this already) + const bool swap_axis = g.IO.KeyShift && !g.IO.ConfigMacOSXBehaviors; + if (swap_axis) + { + wheel.x = wheel.y; + wheel.y = 0.0f; + } + + // Maintain a rough average of moving magnitude on both axises + // FIXME: should by based on wall clock time rather than frame-counter + g.WheelingAxisAvg.x = ImExponentialMovingAverage(g.WheelingAxisAvg.x, ImAbs(wheel.x), 30); + g.WheelingAxisAvg.y = ImExponentialMovingAverage(g.WheelingAxisAvg.y, ImAbs(wheel.y), 30); + + // In the rare situation where FindBestWheelingWindow() had to defer first frame of wheeling due to ambiguous main axis, reinject it now. + wheel += g.WheelingWindowWheelRemainder; + g.WheelingWindowWheelRemainder = ImVec2(0.0f, 0.0f); + if (wheel.x == 0.0f && wheel.y == 0.0f) + return; + + // Mouse wheel scrolling: find target and apply + // - don't renew lock if axis doesn't apply on the window. + // - select a main axis when both axises are being moved. + if (ImGuiWindow* window = (g.WheelingWindow ? g.WheelingWindow : FindBestWheelingWindow(wheel))) + if (!(window->Flags & ImGuiWindowFlags_NoScrollWithMouse) && !(window->Flags & ImGuiWindowFlags_NoMouseInputs)) + { + bool do_scroll[2] = { wheel.x != 0.0f && window->ScrollMax.x != 0.0f, wheel.y != 0.0f && window->ScrollMax.y != 0.0f }; + if (do_scroll[ImGuiAxis_X] && do_scroll[ImGuiAxis_Y]) + do_scroll[(g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? ImGuiAxis_Y : ImGuiAxis_X] = false; + if (do_scroll[ImGuiAxis_X]) + { + LockWheelingWindow(window, wheel.x); + float max_step = window->InnerRect.GetWidth() * 0.67f; + float scroll_step = ImFloor(ImMin(2 * window->CalcFontSize(), max_step)); + SetScrollX(window, window->Scroll.x - wheel.x * scroll_step); + } + if (do_scroll[ImGuiAxis_Y]) + { + LockWheelingWindow(window, wheel.y); + float max_step = window->InnerRect.GetHeight() * 0.67f; + float scroll_step = ImFloor(ImMin(5 * window->CalcFontSize(), max_step)); + SetScrollY(window, window->Scroll.y - wheel.y * scroll_step); + } + } +} + void ImGui::SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard) { ImGuiContext& g = *GImGui; @@ -7849,17 +8555,17 @@ static const char* GetInputSourceName(ImGuiInputSource source) IM_ASSERT(IM_ARRAYSIZE(input_source_names) == ImGuiInputSource_COUNT && source >= 0 && source < ImGuiInputSource_COUNT); return input_source_names[source]; } -#endif - -/*static void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e) +static void DebugPrintInputEvent(const char* prefix, const ImGuiInputEvent* e) { - if (e->Type == ImGuiInputEventType_MousePos) { IMGUI_DEBUG_LOG_IO("%s: MousePos (%.1f %.1f)\n", prefix, e->MousePos.PosX, e->MousePos.PosY); return; } + ImGuiContext& g = *GImGui; + if (e->Type == ImGuiInputEventType_MousePos) { if (e->MousePos.PosX == -FLT_MAX && e->MousePos.PosY == -FLT_MAX) IMGUI_DEBUG_LOG_IO("%s: MousePos (-FLT_MAX, -FLT_MAX)\n", prefix); else IMGUI_DEBUG_LOG_IO("%s: MousePos (%.1f, %.1f)\n", prefix, e->MousePos.PosX, e->MousePos.PosY); return; } if (e->Type == ImGuiInputEventType_MouseButton) { IMGUI_DEBUG_LOG_IO("%s: MouseButton %d %s\n", prefix, e->MouseButton.Button, e->MouseButton.Down ? "Down" : "Up"); return; } - if (e->Type == ImGuiInputEventType_MouseWheel) { IMGUI_DEBUG_LOG_IO("%s: MouseWheel (%.1f %.1f)\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY); return; } + if (e->Type == ImGuiInputEventType_MouseWheel) { IMGUI_DEBUG_LOG_IO("%s: MouseWheel (%.3f, %.3f)\n", prefix, e->MouseWheel.WheelX, e->MouseWheel.WheelY); return; } if (e->Type == ImGuiInputEventType_Key) { IMGUI_DEBUG_LOG_IO("%s: Key \"%s\" %s\n", prefix, ImGui::GetKeyName(e->Key.Key), e->Key.Down ? "Down" : "Up"); return; } if (e->Type == ImGuiInputEventType_Text) { IMGUI_DEBUG_LOG_IO("%s: Text: %c (U+%08X)\n", prefix, e->Text.Char, e->Text.Char); return; } if (e->Type == ImGuiInputEventType_Focus) { IMGUI_DEBUG_LOG_IO("%s: AppFocused %d\n", prefix, e->AppFocused.Focused); return; } -}*/ +} +#endif // Process input queue // We always call this with the value of 'bool g.IO.ConfigInputTrickleEventQueue'. @@ -7882,78 +8588,55 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) int event_n = 0; for (; event_n < g.InputEventsQueue.Size; event_n++) { - const ImGuiInputEvent* e = &g.InputEventsQueue[event_n]; + ImGuiInputEvent* e = &g.InputEventsQueue[event_n]; if (e->Type == ImGuiInputEventType_MousePos) { + // Trickling Rule: Stop processing queued events if we already handled a mouse button change ImVec2 event_pos(e->MousePos.PosX, e->MousePos.PosY); - if (IsMousePosValid(&event_pos)) - event_pos = ImVec2(ImFloorSigned(event_pos.x), ImFloorSigned(event_pos.y)); // Apply same flooring as UpdateMouseInputs() - if (io.MousePos.x != event_pos.x || io.MousePos.y != event_pos.y) - { - // Trickling Rule: Stop processing queued events if we already handled a mouse button change - if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || text_inputted)) - break; - io.MousePos = event_pos; - mouse_moved = true; - } + if (trickle_fast_inputs && (mouse_button_changed != 0 || mouse_wheeled || key_changed || text_inputted)) + break; + io.MousePos = event_pos; + mouse_moved = true; } else if (e->Type == ImGuiInputEventType_MouseButton) { + // Trickling Rule: Stop processing queued events if we got multiple action on the same button const ImGuiMouseButton button = e->MouseButton.Button; IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT); - if (io.MouseDown[button] != e->MouseButton.Down) - { - // Trickling Rule: Stop processing queued events if we got multiple action on the same button - if (trickle_fast_inputs && ((mouse_button_changed & (1 << button)) || mouse_wheeled)) - break; - io.MouseDown[button] = e->MouseButton.Down; - mouse_button_changed |= (1 << button); - } + if (trickle_fast_inputs && ((mouse_button_changed & (1 << button)) || mouse_wheeled)) + break; + io.MouseDown[button] = e->MouseButton.Down; + mouse_button_changed |= (1 << button); } else if (e->Type == ImGuiInputEventType_MouseWheel) { - if (e->MouseWheel.WheelX != 0.0f || e->MouseWheel.WheelY != 0.0f) - { - // Trickling Rule: Stop processing queued events if we got multiple action on the event - if (trickle_fast_inputs && (mouse_moved || mouse_button_changed != 0)) - break; - io.MouseWheelH += e->MouseWheel.WheelX; - io.MouseWheel += e->MouseWheel.WheelY; - mouse_wheeled = true; - } + // Trickling Rule: Stop processing queued events if we got multiple action on the event + if (trickle_fast_inputs && (mouse_moved || mouse_button_changed != 0)) + break; + io.MouseWheelH += e->MouseWheel.WheelX; + io.MouseWheel += e->MouseWheel.WheelY; + mouse_wheeled = true; } else if (e->Type == ImGuiInputEventType_Key) { + // Trickling Rule: Stop processing queued events if we got multiple action on the same button ImGuiKey key = e->Key.Key; IM_ASSERT(key != ImGuiKey_None); - const int keydata_index = (key - ImGuiKey_KeysData_OFFSET); - ImGuiKeyData* keydata = &io.KeysData[keydata_index]; - if (keydata->Down != e->Key.Down || keydata->AnalogValue != e->Key.AnalogValue) - { - // Trickling Rule: Stop processing queued events if we got multiple action on the same button - if (trickle_fast_inputs && keydata->Down != e->Key.Down && (key_changed_mask.TestBit(keydata_index) || text_inputted || mouse_button_changed != 0)) - break; - keydata->Down = e->Key.Down; - keydata->AnalogValue = e->Key.AnalogValue; - key_changed = true; - key_changed_mask.SetBit(keydata_index); + ImGuiKeyData* key_data = GetKeyData(key); + const int key_data_index = (int)(key_data - g.IO.KeysData); + if (trickle_fast_inputs && key_data->Down != e->Key.Down && (key_changed_mask.TestBit(key_data_index) || text_inputted || mouse_button_changed != 0)) + break; + key_data->Down = e->Key.Down; + key_data->AnalogValue = e->Key.AnalogValue; + key_changed = true; + key_changed_mask.SetBit(key_data_index); - if (key == ImGuiKey_ModCtrl || key == ImGuiKey_ModShift || key == ImGuiKey_ModAlt || key == ImGuiKey_ModSuper) - { - if (key == ImGuiKey_ModCtrl) { io.KeyCtrl = keydata->Down; } - if (key == ImGuiKey_ModShift) { io.KeyShift = keydata->Down; } - if (key == ImGuiKey_ModAlt) { io.KeyAlt = keydata->Down; } - if (key == ImGuiKey_ModSuper) { io.KeySuper = keydata->Down; } - io.KeyMods = GetMergedModFlags(); - } - - // Allow legacy code using io.KeysDown[GetKeyIndex()] with new backends + // Allow legacy code using io.KeysDown[GetKeyIndex()] with new backends #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - io.KeysDown[key] = keydata->Down; - if (io.KeyMap[key] != -1) - io.KeysDown[io.KeyMap[key]] = keydata->Down; + io.KeysDown[key_data_index] = key_data->Down; + if (io.KeyMap[key_data_index] != -1) + io.KeysDown[io.KeyMap[key_data_index]] = key_data->Down; #endif - } } else if (e->Type == ImGuiInputEventType_Text) { @@ -7967,9 +8650,10 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) } else if (e->Type == ImGuiInputEventType_Focus) { - // We intentionally overwrite this and process lower, in order to give a chance + // We intentionally overwrite this and process in NewFrame(), in order to give a chance // to multi-viewports backends to queue AddFocusEvent(false) + AddFocusEvent(true) in same frame. - io.AppFocusLost = !e->AppFocused.Focused; + const bool focus_lost = !e->AppFocused.Focused; + io.AppFocusLost = focus_lost; } else { @@ -7983,9 +8667,11 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) g.InputEventsTrail.push_back(g.InputEventsQueue[n]); // [DEBUG] - /*if (event_n != 0) +#ifndef IMGUI_DISABLE_DEBUG_TOOLS + if (event_n != 0 && (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO)) for (int n = 0; n < g.InputEventsQueue.Size; n++) - DebugPrintInputEvent(n < event_n ? "Processed" : "Remaining", &g.InputEventsQueue[n]);*/ + DebugPrintInputEvent(n < event_n ? "Processed" : "Remaining", &g.InputEventsQueue[n]); +#endif // Remaining events will be processed on the next frame if (event_n == g.InputEventsQueue.Size) @@ -7994,12 +8680,127 @@ void ImGui::UpdateInputEvents(bool trickle_fast_inputs) g.InputEventsQueue.erase(g.InputEventsQueue.Data, g.InputEventsQueue.Data + event_n); // Clear buttons state when focus is lost - // (this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle) + // - this is useful so e.g. releasing Alt after focus loss on Alt-Tab doesn't trigger the Alt menu toggle. + // - we clear in EndFrame() and not now in order allow application/user code polling this flag + // (e.g. custom backend may want to clear additional data, custom widgets may want to react with a "canceling" event). if (g.IO.AppFocusLost) - { g.IO.ClearInputKeys(); - g.IO.AppFocusLost = false; +} + +ImGuiID ImGui::GetKeyOwner(ImGuiKey key) +{ + if (!IsNamedKeyOrModKey(key)) + return ImGuiKeyOwner_None; + + ImGuiContext& g = *GImGui; + ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(key); + ImGuiID owner_id = owner_data->OwnerCurr; + + if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any) + if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END) + return ImGuiKeyOwner_None; + + return owner_id; +} + +// TestKeyOwner(..., ID) : (owner == None || owner == ID) +// TestKeyOwner(..., None) : (owner == None) +// TestKeyOwner(..., Any) : no owner test +// All paths are also testing for key not being locked, for the rare cases that key have been locked with using ImGuiInputFlags_LockXXX flags. +bool ImGui::TestKeyOwner(ImGuiKey key, ImGuiID owner_id) +{ + if (!IsNamedKeyOrModKey(key)) + return true; + + ImGuiContext& g = *GImGui; + if (g.ActiveIdUsingAllKeyboardKeys && owner_id != g.ActiveId && owner_id != ImGuiKeyOwner_Any) + if (key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END) + return false; + + ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(key); + if (owner_id == ImGuiKeyOwner_Any) + return (owner_data->LockThisFrame == false); + + // Note: SetKeyOwner() sets OwnerCurr. It is not strictly required for most mouse routing overlap (because of ActiveId/HoveredId + // are acting as filter before this has a chance to filter), but sane as soon as user tries to look into things. + // Setting OwnerCurr in SetKeyOwner() is more consistent than testing OwnerNext here: would be inconsistent with getter and other functions. + if (owner_data->OwnerCurr != owner_id) + { + if (owner_data->LockThisFrame) + return false; + if (owner_data->OwnerCurr != ImGuiKeyOwner_None) + return false; } + + return true; +} + +// _LockXXX flags are useful to lock keys away from code which is not input-owner aware. +// When using _LockXXX flags, you can use ImGuiKeyOwner_Any to lock keys from everyone. +// - SetKeyOwner(..., None) : clears owner +// - SetKeyOwner(..., Any, !Lock) : illegal (assert) +// - SetKeyOwner(..., Any or None, Lock) : set lock +void ImGui::SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags) +{ + IM_ASSERT(IsNamedKeyOrModKey(key) && (owner_id != ImGuiKeyOwner_Any || (flags & (ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease)))); // Can only use _Any with _LockXXX flags (to eat a key away without an ID to retrieve it) + IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetKeyOwner) == 0); // Passing flags not supported by this function! + + ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(key); + owner_data->OwnerCurr = owner_data->OwnerNext = owner_id; + + // We cannot lock by default as it would likely break lots of legacy code. + // In the case of using LockUntilRelease while key is not down we still lock during the frame (no key_data->Down test) + owner_data->LockUntilRelease = (flags & ImGuiInputFlags_LockUntilRelease) != 0; + owner_data->LockThisFrame = (flags & ImGuiInputFlags_LockThisFrame) != 0 || (owner_data->LockUntilRelease); +} + +// This is more or less equivalent to: +// if (IsItemHovered() || IsItemActive()) +// SetKeyOwner(key, GetItemID()); +// Extensive uses of that (e.g. many calls for a single item) may want to manually perform the tests once and then call SetKeyOwner() multiple times. +// More advanced usage scenarios may want to call SetKeyOwner() manually based on different condition. +// Worth noting is that only one item can be hovered and only one item can be active, therefore this usage pattern doesn't need to bother with routing and priority. +void ImGui::SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags) +{ + ImGuiContext& g = *GImGui; + ImGuiID id = g.LastItemData.ID; + if (id == 0 || (g.HoveredId != id && g.ActiveId != id)) + return; + if ((flags & ImGuiInputFlags_CondMask_) == 0) + flags |= ImGuiInputFlags_CondDefault_; + if ((g.HoveredId == id && (flags & ImGuiInputFlags_CondHovered)) || (g.ActiveId == id && (flags & ImGuiInputFlags_CondActive))) + { + IM_ASSERT((flags & ~ImGuiInputFlags_SupportedBySetItemKeyOwner) == 0); // Passing flags not supported by this function! + SetKeyOwner(key, id, flags & ~ImGuiInputFlags_CondMask_); + } +} + +bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id, ImGuiInputFlags flags) +{ + ImGuiContext& g = *GImGui; + + // When using (owner_id == 0/Any): SetShortcutRouting() will use CurrentFocusScopeId and filter with this, so IsKeyPressed() is fine with he 0/Any. + if ((flags & ImGuiInputFlags_RouteMask_) == 0) + flags |= ImGuiInputFlags_RouteFocused; + if (!SetShortcutRouting(key_chord, owner_id, flags)) + return false; + + if (key_chord & ImGuiMod_Shortcut) + key_chord = ConvertShortcutMod(key_chord); + ImGuiKey mods = (ImGuiKey)(key_chord & ImGuiMod_Mask_); + if (g.IO.KeyMods != mods) + return false; + + // Special storage location for mods + ImGuiKey key = (ImGuiKey)(key_chord & ~ImGuiMod_Mask_); + if (key == ImGuiKey_None) + key = ConvertSingleModFlagToKey(mods); + + if (!IsKeyPressed(key, owner_id, (flags & (ImGuiInputFlags_Repeat | (ImGuiInputFlags)ImGuiInputFlags_RepeatRateMask_)))) + return false; + IM_ASSERT((flags & ~ImGuiInputFlags_SupportedByShortcut) == 0); // Passing flags not supported by this function! + + return true; } @@ -8028,6 +8829,38 @@ bool ImGui::DebugCheckVersionAndDataLayout(const char* version, size_t sz_io, si return !error; } +// Until 1.89 (IMGUI_VERSION_NUM < 18814) it was legal to use SetCursorPos() to extend the boundary of a parent (e.g. window or table cell) +// This is causing issues and ambiguity and we need to retire that. +// See https://github.com/ocornut/imgui/issues/5548 for more details. +// [Scenario 1] +// Previously this would make the window content size ~200x200: +// Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + End(); // NOT OK +// Instead, please submit an item: +// Begin(...) + SetCursorScreenPos(GetCursorScreenPos() + ImVec2(200,200)) + Dummy(ImVec2(0,0)) + End(); // OK +// Alternative: +// Begin(...) + Dummy(ImVec2(200,200)) + End(); // OK +// [Scenario 2] +// For reference this is one of the issue what we aim to fix with this change: +// BeginGroup() + SomeItem("foobar") + SetCursorScreenPos(GetCursorScreenPos()) + EndGroup() +// The previous logic made SetCursorScreenPos(GetCursorScreenPos()) have a side-effect! It would erroneously incorporate ItemSpacing.y after the item into content size, making the group taller! +// While this code is a little twisted, no-one would expect SetXXX(GetXXX()) to have a side-effect. Using vertical alignment patterns could trigger this issue. +void ImGui::ErrorCheckUsingSetCursorPosToExtendParentBoundaries() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + IM_ASSERT(window->DC.IsSetPos); + window->DC.IsSetPos = false; +#ifdef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + if (window->DC.CursorPos.x <= window->DC.CursorMaxPos.x && window->DC.CursorPos.y <= window->DC.CursorMaxPos.y) + return; + if (window->SkipItems) + return; + IM_ASSERT(0 && "Code uses SetCursorPos()/SetCursorScreenPos() to extend window/parent boundaries. Please submit an item e.g. Dummy() to validate extent."); +#else + window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); +#endif +} + static void ImGui::ErrorCheckNewFrameSanityChecks() { ImGuiContext& g = *GImGui; @@ -8075,9 +8908,9 @@ static void ImGui::ErrorCheckEndFrameSanityChecks() // One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame(). // It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will // send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs. - // We silently accommodate for this case by ignoring/ the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0), + // We silently accommodate for this case by ignoring the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0), // while still correctly asserting on mid-frame key press events. - const ImGuiModFlags key_mods = GetMergedModFlags(); + const ImGuiKeyChord key_mods = GetMergedModsFromKeys(); IM_ASSERT((key_mods == 0 || g.IO.KeyMods == key_mods) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods"); IM_UNUSED(key_mods); @@ -8187,7 +9020,7 @@ void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name); PopStyleVar(); } - while (g.FocusScopeStack.Size > stack_sizes->SizeOfFocusScopeStack) //-V1044 + while (g.FocusScopeStack.Size > stack_sizes->SizeOfFocusScopeStack + 1) //-V1044 { if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name); PopFocusScope(); @@ -8289,7 +9122,7 @@ void ImGui::ItemSize(const ImVec2& size, float text_baseline_y) window->DC.CursorPosPrevLine.x = window->DC.CursorPos.x + size.x; window->DC.CursorPosPrevLine.y = line_y1; window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); // Next line - window->DC.CursorPos.y = IM_FLOOR(line_y1 + line_height + g.Style.ItemSpacing.y); // Next line + window->DC.CursorPos.y = IM_FLOOR(line_y1 + line_height + g.Style.ItemSpacing.y); // Next line window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPosPrevLine.x); window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y - g.Style.ItemSpacing.y); //if (g.IO.KeyAlt) window->DrawList->AddCircle(window->DC.CursorMaxPos, 3.0f, IM_COL32(255,0,0,255), 4); // [DEBUG] @@ -8298,7 +9131,7 @@ void ImGui::ItemSize(const ImVec2& size, float text_baseline_y) window->DC.CurrLineSize.y = 0.0f; window->DC.PrevLineTextBaseOffset = ImMax(window->DC.CurrLineTextBaseOffset, text_baseline_y); window->DC.CurrLineTextBaseOffset = 0.0f; - window->DC.IsSameLine = false; + window->DC.IsSameLine = window->DC.IsSetPos = false; // Horizontal layout mode if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) @@ -8335,25 +9168,19 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu // to reach unclipped widgets. This would work if user had explicit scrolling control (e.g. mapped on a stick). // We intentionally don't check if g.NavWindow != NULL because g.NavAnyRequest should only be set when it is non null. // If we crash on a NULL g.NavWindow we need to fix the bug elsewhere. - window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent); - if (g.NavId == id || g.NavAnyRequest) - if (g.NavWindow->RootWindowForNav == window->RootWindowForNav) - if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened)) - NavProcessItem(); + if (!(g.LastItemData.InFlags & ImGuiItemFlags_NoNav)) + { + window->DC.NavLayersActiveMaskNext |= (1 << window->DC.NavLayerCurrent); + if (g.NavId == id || g.NavAnyRequest) + if (g.NavWindow->RootWindowForNav == window->RootWindowForNav) + if (window == g.NavWindow || ((window->Flags | g.NavWindow->Flags) & ImGuiWindowFlags_NavFlattened)) + NavProcessItem(); + } // [DEBUG] People keep stumbling on this problem and using "" as identifier in the root of a window instead of "##something". // Empty identifier are valid and useful in a small amount of cases, but 99.9% of the time you want to use "##something". // READ THE FAQ: https://dearimgui.org/faq IM_ASSERT(id != window->ID && "Cannot have an empty ID at the root of a window. If you need an empty label, use ## and read the FAQ about how the ID Stack works!"); - - // [DEBUG] Item Picker tool, when enabling the "extended" version we perform the check in ItemAdd() -#ifdef IMGUI_DEBUG_TOOL_ITEM_PICKER_EX - if (id == g.DebugItemPickerBreakId) - { - IM_DEBUG_BREAK(); - g.DebugItemPickerBreakId = 0; - } -#endif } g.NextItemData.Flags = ImGuiNextItemDataFlags_None; @@ -8363,12 +9190,26 @@ bool ImGui::ItemAdd(const ImRect& bb, ImGuiID id, const ImRect* nav_bb_arg, ImGu #endif // Clipping test - const bool is_clipped = IsClippedEx(bb, id); - if (is_clipped) - return false; + // (FIXME: This is a modified copy of IsClippedEx() so we can reuse the is_rect_visible value) + //const bool is_clipped = IsClippedEx(bb, id); + //if (is_clipped) + // return false; + const bool is_rect_visible = bb.Overlaps(window->ClipRect); + if (!is_rect_visible) + if (id == 0 || (id != g.ActiveId && id != g.NavId)) + if (!g.LogEnabled) + return false; + + // [DEBUG] +#ifndef IMGUI_DISABLE_DEBUG_TOOLS + if (id != 0 && id == g.DebugLocateId) + DebugLocateItemResolveWithLastItem(); +#endif //if (g.IO.KeyAlt) window->DrawList->AddRect(bb.Min, bb.Max, IM_COL32(255,255,0,120)); // [DEBUG] // We need to calculate this now to take account of the current clipping rectangle (as items like Selectable may change them) + if (is_rect_visible) + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_Visible; if (IsMouseHoveringRect(bb.Min, bb.Max)) g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredRect; return true; @@ -8411,11 +9252,15 @@ ImVec2 ImGui::GetCursorScreenPos() return window->DC.CursorPos; } +// 2022/08/05: Setting cursor position also extend boundaries (via modifying CursorMaxPos) used to compute window size, group size etc. +// I believe this was is a judicious choice but it's probably being relied upon (it has been the case since 1.31 and 1.50) +// It would be sane if we requested user to use SetCursorPos() + Dummy(ImVec2(0,0)) to extend CursorMaxPos... void ImGui::SetCursorScreenPos(const ImVec2& pos) { ImGuiWindow* window = GetCurrentWindow(); window->DC.CursorPos = pos; - window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); + //window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); + window->DC.IsSetPos = true; } // User generally sees positions in window coordinates. Internally we store CursorPos in absolute screen coordinates because it is more convenient. @@ -8442,21 +9287,24 @@ void ImGui::SetCursorPos(const ImVec2& local_pos) { ImGuiWindow* window = GetCurrentWindow(); window->DC.CursorPos = window->Pos - window->Scroll + local_pos; - window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); + //window->DC.CursorMaxPos = ImMax(window->DC.CursorMaxPos, window->DC.CursorPos); + window->DC.IsSetPos = true; } void ImGui::SetCursorPosX(float x) { ImGuiWindow* window = GetCurrentWindow(); window->DC.CursorPos.x = window->Pos.x - window->Scroll.x + x; - window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x); + //window->DC.CursorMaxPos.x = ImMax(window->DC.CursorMaxPos.x, window->DC.CursorPos.x); + window->DC.IsSetPos = true; } void ImGui::SetCursorPosY(float y) { ImGuiWindow* window = GetCurrentWindow(); window->DC.CursorPos.y = window->Pos.y - window->Scroll.y + y; - window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y); + //window->DC.CursorMaxPos.y = ImMax(window->DC.CursorMaxPos.y, window->DC.CursorPos.y); + window->DC.IsSetPos = true; } ImVec2 ImGui::GetCursorStartPos() @@ -8673,6 +9521,9 @@ void ImGui::EndGroup() ImGuiGroupData& group_data = g.GroupStack.back(); IM_ASSERT(group_data.WindowID == window->ID); // EndGroup() in wrong window? + if (window->DC.IsSetPos) + ErrorCheckUsingSetCursorPosToExtendParentBoundaries(); + ImRect group_bb(group_data.BackupCursorPos, ImMax(window->DC.CursorMaxPos, group_data.BackupCursorPos)); window->DC.CursorPos = group_data.BackupCursorPos; @@ -8695,7 +9546,7 @@ void ImGui::EndGroup() ItemAdd(group_bb, 0, NULL, ImGuiItemFlags_NoTabStop); // If the current ActiveId was declared within the boundary of our group, we copy it to LastItemId so IsItemActive(), IsItemDeactivated() etc. will be functional on the entire group. - // It would be be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but would put a little more burden on individual widgets. + // It would be neater if we replaced window.DC.LastItemId by e.g. 'bool LastItemIsActive', but would put a little more burden on individual widgets. // Also if you grep for LastItemId you'll notice it is only used in that context. // (The two tests not the same because ActiveIdIsAlive is an ID itself, in order to be able to handle ActiveId being overwritten during the frame.) const bool group_contains_curr_active_id = (group_data.BackupActiveIdIsAlive != g.ActiveId) && (g.ActiveIdIsAlive == g.ActiveId) && g.ActiveId; @@ -8745,38 +9596,24 @@ static float CalcScrollEdgeSnap(float target, float snap_min, float snap_max, fl static ImVec2 CalcNextScrollFromScrollTargetAndClamp(ImGuiWindow* window) { ImVec2 scroll = window->Scroll; - if (window->ScrollTarget.x < FLT_MAX) + ImVec2 decoration_size(window->DecoOuterSizeX1 + window->DecoInnerSizeX1 + window->DecoOuterSizeX2, window->DecoOuterSizeY1 + window->DecoInnerSizeY1 + window->DecoOuterSizeY2); + for (int axis = 0; axis < 2; axis++) { - float decoration_total_width = window->ScrollbarSizes.x; - float center_x_ratio = window->ScrollTargetCenterRatio.x; - float scroll_target_x = window->ScrollTarget.x; - if (window->ScrollTargetEdgeSnapDist.x > 0.0f) + if (window->ScrollTarget[axis] < FLT_MAX) { - float snap_x_min = 0.0f; - float snap_x_max = window->ScrollMax.x + window->SizeFull.x - decoration_total_width; - scroll_target_x = CalcScrollEdgeSnap(scroll_target_x, snap_x_min, snap_x_max, window->ScrollTargetEdgeSnapDist.x, center_x_ratio); + float center_ratio = window->ScrollTargetCenterRatio[axis]; + float scroll_target = window->ScrollTarget[axis]; + if (window->ScrollTargetEdgeSnapDist[axis] > 0.0f) + { + float snap_min = 0.0f; + float snap_max = window->ScrollMax[axis] + window->SizeFull[axis] - decoration_size[axis]; + scroll_target = CalcScrollEdgeSnap(scroll_target, snap_min, snap_max, window->ScrollTargetEdgeSnapDist[axis], center_ratio); + } + scroll[axis] = scroll_target - center_ratio * (window->SizeFull[axis] - decoration_size[axis]); } - scroll.x = scroll_target_x - center_x_ratio * (window->SizeFull.x - decoration_total_width); - } - if (window->ScrollTarget.y < FLT_MAX) - { - float decoration_total_height = window->TitleBarHeight() + window->MenuBarHeight() + window->ScrollbarSizes.y; - float center_y_ratio = window->ScrollTargetCenterRatio.y; - float scroll_target_y = window->ScrollTarget.y; - if (window->ScrollTargetEdgeSnapDist.y > 0.0f) - { - float snap_y_min = 0.0f; - float snap_y_max = window->ScrollMax.y + window->SizeFull.y - decoration_total_height; - scroll_target_y = CalcScrollEdgeSnap(scroll_target_y, snap_y_min, snap_y_max, window->ScrollTargetEdgeSnapDist.y, center_y_ratio); - } - scroll.y = scroll_target_y - center_y_ratio * (window->SizeFull.y - decoration_total_height); - } - scroll.x = IM_FLOOR(ImMax(scroll.x, 0.0f)); - scroll.y = IM_FLOOR(ImMax(scroll.y, 0.0f)); - if (!window->Collapsed && !window->SkipItems) - { - scroll.x = ImMin(scroll.x, window->ScrollMax.x); - scroll.y = ImMin(scroll.y, window->ScrollMax.y); + scroll[axis] = IM_FLOOR(ImMax(scroll[axis], 0.0f)); + if (!window->Collapsed && !window->SkipItems) + scroll[axis] = ImMin(scroll[axis], window->ScrollMax[axis]); } return scroll; } @@ -8797,8 +9634,11 @@ void ImGui::ScrollToRect(ImGuiWindow* window, const ImRect& item_rect, ImGuiScro ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGuiScrollFlags flags) { ImGuiContext& g = *GImGui; - ImRect window_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1)); - //GetForegroundDrawList(window)->AddRect(window_rect.Min, window_rect.Max, IM_COL32_WHITE); // [DEBUG] + ImRect scroll_rect(window->InnerRect.Min - ImVec2(1, 1), window->InnerRect.Max + ImVec2(1, 1)); + scroll_rect.Min.x = ImMin(scroll_rect.Min.x + window->DecoInnerSizeX1, scroll_rect.Max.x); + scroll_rect.Min.y = ImMin(scroll_rect.Min.y + window->DecoInnerSizeY1, scroll_rect.Max.y); + //GetForegroundDrawList(window)->AddRect(item_rect.Min, item_rect.Max, IM_COL32(255,0,0,255), 0.0f, 0, 5.0f); // [DEBUG] + //GetForegroundDrawList(window)->AddRect(scroll_rect.Min, scroll_rect.Max, IM_COL32_WHITE); // [DEBUG] // Check that only one behavior is selected per axis IM_ASSERT((flags & ImGuiScrollFlags_MaskX_) == 0 || ImIsPowerOfTwo(flags & ImGuiScrollFlags_MaskX_)); @@ -8811,35 +9651,39 @@ ImVec2 ImGui::ScrollToRectEx(ImGuiWindow* window, const ImRect& item_rect, ImGui if ((flags & ImGuiScrollFlags_MaskY_) == 0) flags |= window->Appearing ? ImGuiScrollFlags_AlwaysCenterY : ImGuiScrollFlags_KeepVisibleEdgeY; - const bool fully_visible_x = item_rect.Min.x >= window_rect.Min.x && item_rect.Max.x <= window_rect.Max.x; - const bool fully_visible_y = item_rect.Min.y >= window_rect.Min.y && item_rect.Max.y <= window_rect.Max.y; - const bool can_be_fully_visible_x = (item_rect.GetWidth() + g.Style.ItemSpacing.x * 2.0f) <= window_rect.GetWidth(); - const bool can_be_fully_visible_y = (item_rect.GetHeight() + g.Style.ItemSpacing.y * 2.0f) <= window_rect.GetHeight(); + const bool fully_visible_x = item_rect.Min.x >= scroll_rect.Min.x && item_rect.Max.x <= scroll_rect.Max.x; + const bool fully_visible_y = item_rect.Min.y >= scroll_rect.Min.y && item_rect.Max.y <= scroll_rect.Max.y; + const bool can_be_fully_visible_x = (item_rect.GetWidth() + g.Style.ItemSpacing.x * 2.0f) <= scroll_rect.GetWidth() || (window->AutoFitFramesX > 0) || (window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0; + const bool can_be_fully_visible_y = (item_rect.GetHeight() + g.Style.ItemSpacing.y * 2.0f) <= scroll_rect.GetHeight() || (window->AutoFitFramesY > 0) || (window->Flags & ImGuiWindowFlags_AlwaysAutoResize) != 0; if ((flags & ImGuiScrollFlags_KeepVisibleEdgeX) && !fully_visible_x) { - if (item_rect.Min.x < window_rect.Min.x || !can_be_fully_visible_x) + if (item_rect.Min.x < scroll_rect.Min.x || !can_be_fully_visible_x) SetScrollFromPosX(window, item_rect.Min.x - g.Style.ItemSpacing.x - window->Pos.x, 0.0f); - else if (item_rect.Max.x >= window_rect.Max.x) + else if (item_rect.Max.x >= scroll_rect.Max.x) SetScrollFromPosX(window, item_rect.Max.x + g.Style.ItemSpacing.x - window->Pos.x, 1.0f); } else if (((flags & ImGuiScrollFlags_KeepVisibleCenterX) && !fully_visible_x) || (flags & ImGuiScrollFlags_AlwaysCenterX)) { - float target_x = can_be_fully_visible_x ? ImFloor((item_rect.Min.x + item_rect.Max.x - window->InnerRect.GetWidth()) * 0.5f) : item_rect.Min.x; - SetScrollFromPosX(window, target_x - window->Pos.x, 0.0f); + if (can_be_fully_visible_x) + SetScrollFromPosX(window, ImFloor((item_rect.Min.x + item_rect.Max.x) * 0.5f) - window->Pos.x, 0.5f); + else + SetScrollFromPosX(window, item_rect.Min.x - window->Pos.x, 0.0f); } if ((flags & ImGuiScrollFlags_KeepVisibleEdgeY) && !fully_visible_y) { - if (item_rect.Min.y < window_rect.Min.y || !can_be_fully_visible_y) + if (item_rect.Min.y < scroll_rect.Min.y || !can_be_fully_visible_y) SetScrollFromPosY(window, item_rect.Min.y - g.Style.ItemSpacing.y - window->Pos.y, 0.0f); - else if (item_rect.Max.y >= window_rect.Max.y) + else if (item_rect.Max.y >= scroll_rect.Max.y) SetScrollFromPosY(window, item_rect.Max.y + g.Style.ItemSpacing.y - window->Pos.y, 1.0f); } else if (((flags & ImGuiScrollFlags_KeepVisibleCenterY) && !fully_visible_y) || (flags & ImGuiScrollFlags_AlwaysCenterY)) { - float target_y = can_be_fully_visible_y ? ImFloor((item_rect.Min.y + item_rect.Max.y - window->InnerRect.GetHeight()) * 0.5f) : item_rect.Min.y; - SetScrollFromPosY(window, target_y - window->Pos.y, 0.0f); + if (can_be_fully_visible_y) + SetScrollFromPosY(window, ImFloor((item_rect.Min.y + item_rect.Max.y) * 0.5f) - window->Pos.y, 0.5f); + else + SetScrollFromPosY(window, item_rect.Min.y - window->Pos.y, 0.0f); } ImVec2 next_scroll = CalcNextScrollFromScrollTargetAndClamp(window); @@ -8914,7 +9758,7 @@ void ImGui::SetScrollY(float scroll_y) // - local_pos = (absolution_pos - window->Pos) // - So local_x/local_y are 0.0f for a position at the upper-left corner of a window, // and generally local_x/local_y are >(padding+decoration) && <(size-padding-decoration) when in the visible area. -// - They mostly exists because of legacy API. +// - They mostly exist because of legacy API. // Following the rules above, when trying to work with scrolling code, consider that: // - SetScrollFromPosY(0.0f) == SetScrollY(0.0f + scroll.y) == has no effect! // - SetScrollFromPosY(-scroll.y) == SetScrollY(-scroll.y + scroll.y) == SetScrollY(0.0f) == reset scroll. Of course writing SetScrollY(0.0f) directly then makes more sense @@ -8922,7 +9766,7 @@ void ImGui::SetScrollY(float scroll_y) void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio) { IM_ASSERT(center_x_ratio >= 0.0f && center_x_ratio <= 1.0f); - window->ScrollTarget.x = IM_FLOOR(local_x + window->Scroll.x); // Convert local position to scroll offset + window->ScrollTarget.x = IM_FLOOR(local_x - window->DecoOuterSizeX1 - window->DecoInnerSizeX1 + window->Scroll.x); // Convert local position to scroll offset window->ScrollTargetCenterRatio.x = center_x_ratio; window->ScrollTargetEdgeSnapDist.x = 0.0f; } @@ -8930,9 +9774,7 @@ void ImGui::SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x void ImGui::SetScrollFromPosY(ImGuiWindow* window, float local_y, float center_y_ratio) { IM_ASSERT(center_y_ratio >= 0.0f && center_y_ratio <= 1.0f); - const float decoration_up_height = window->TitleBarHeight() + window->MenuBarHeight(); // FIXME: Would be nice to have a more standardized access to our scrollable/client rect; - local_y -= decoration_up_height; - window->ScrollTarget.y = IM_FLOOR(local_y + window->Scroll.y); // Convert local position to scroll offset + window->ScrollTarget.y = IM_FLOOR(local_y - window->DecoOuterSizeY1 - window->DecoInnerSizeY1 + window->Scroll.y); // Convert local position to scroll offset window->ScrollTargetCenterRatio.y = center_y_ratio; window->ScrollTargetEdgeSnapDist.y = 0.0f; } @@ -9132,7 +9974,7 @@ void ImGui::OpenPopupEx(ImGuiID id, ImGuiPopupFlags popup_flags) ImGuiPopupData popup_ref; // Tagged as new ref as Window will be set back to NULL if we write this into OpenPopupStack. popup_ref.PopupId = id; popup_ref.Window = NULL; - popup_ref.SourceWindow = g.NavWindow; + popup_ref.BackupNavWindow = g.NavWindow; // When popup closes focus may be restored to NavWindow (depend on window type). popup_ref.OpenFrameCount = g.FrameCount; popup_ref.OpenParentId = parent_window->IDStack.back(); popup_ref.OpenPopupPos = NavCalcPreferredRefPos(); @@ -9234,12 +10076,13 @@ void ImGui::ClosePopupToLevel(int remaining, bool restore_focus_to_window_under_ IM_ASSERT(remaining >= 0 && remaining < g.OpenPopupStack.Size); // Trim open popup stack - ImGuiWindow* focus_window = g.OpenPopupStack[remaining].SourceWindow; ImGuiWindow* popup_window = g.OpenPopupStack[remaining].Window; + ImGuiWindow* popup_backup_nav_window = g.OpenPopupStack[remaining].BackupNavWindow; g.OpenPopupStack.resize(remaining); if (restore_focus_to_window_under_popup) { + ImGuiWindow* focus_window = (popup_window && popup_window->Flags & ImGuiWindowFlags_ChildMenu) ? popup_window->ParentWindow : popup_backup_nav_window; if (focus_window && !focus_window->WasActive && popup_window) { // Fallback @@ -9494,7 +10337,7 @@ ImVec2 ImGui::FindBestWindowPosForPopupEx(const ImVec2& ref_pos, const ImVec2& s const float avail_w = (dir == ImGuiDir_Left ? r_avoid.Min.x : r_outer.Max.x) - (dir == ImGuiDir_Right ? r_avoid.Max.x : r_outer.Min.x); const float avail_h = (dir == ImGuiDir_Up ? r_avoid.Min.y : r_outer.Max.y) - (dir == ImGuiDir_Down ? r_avoid.Max.y : r_outer.Min.y); - // If there not enough room on one axis, there's no point in positioning on a side on this axis (e.g. when not enough width, use a top/bottom position to maximize available width) + // If there's not enough room on one axis, there's no point in positioning on a side on this axis (e.g. when not enough width, use a top/bottom position to maximize available width) if (avail_w < size.x && (dir == ImGuiDir_Left || dir == ImGuiDir_Right)) continue; if (avail_h < size.y && (dir == ImGuiDir_Up || dir == ImGuiDir_Down)) @@ -9617,12 +10460,12 @@ void ImGui::SetFocusID(ImGuiID id, ImGuiWindow* window) if (g.NavWindow != window) SetNavWindow(window); - // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and window->DC.NavFocusScopeIdCurrent are valid. + // Assume that SetFocusID() is called in the context where its window->DC.NavLayerCurrent and g.CurrentFocusScopeId are valid. // Note that window may be != g.CurrentWindow (e.g. SetFocusID call in InputTextEx for multi-line text) const ImGuiNavLayer nav_layer = window->DC.NavLayerCurrent; g.NavId = id; g.NavLayer = nav_layer; - g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; + g.NavFocusScopeId = g.CurrentFocusScopeId; window->NavLastIds[nav_layer] = id; if (g.LastItemData.ID == id) window->NavRectRel[nav_layer] = WindowRectAbsToRel(window, g.LastItemData.NavRect); @@ -9686,7 +10529,7 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result) } // We perform scoring on items bounding box clipped by the current clipping rectangle on the other axis (clipping on our movement axis would give us equal scores for all clipped items) - // For example, this ensure that items in one column are not reached when moving vertically from items in another column. + // For example, this ensures that items in one column are not reached when moving vertically from items in another column. NavClampRectToVisibleAreaForMoveDir(g.NavMoveClipDir, cand, window->ClipRect); // Compute distance between boxes @@ -9750,7 +10593,7 @@ static bool ImGui::NavScoreItem(ImGuiNavItemData* result) } #endif - // Is it in the quadrant we're interesting in moving to? + // Is it in the quadrant we're interested in moving to? bool new_best = false; const ImGuiDir move_dir = g.NavMoveDir; if (quadrant == move_dir) @@ -9803,7 +10646,7 @@ static void ImGui::NavApplyItemToResult(ImGuiNavItemData* result) ImGuiWindow* window = g.CurrentWindow; result->Window = window; result->ID = g.LastItemData.ID; - result->FocusScopeId = window->DC.NavFocusScopeIdCurrent; + result->FocusScopeId = g.CurrentFocusScopeId; result->InFlags = g.LastItemData.InFlags; result->RectRel = WindowRectAbsToRel(window, g.LastItemData.NavRect); } @@ -9846,7 +10689,7 @@ static void ImGui::NavProcessItem() if (is_tab_stop || (g.NavMoveFlags & ImGuiNavMoveFlags_FocusApi)) NavProcessItemForTabbingRequest(id); } - else if ((g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & (ImGuiItemFlags_Disabled | ImGuiItemFlags_NoNav))) + else if ((g.NavId != id || (g.NavMoveFlags & ImGuiNavMoveFlags_AllowCurrentNavId)) && !(item_flags & ImGuiItemFlags_Disabled)) { ImGuiNavItemData* result = (window == g.NavWindow) ? &g.NavMoveResultLocal : &g.NavMoveResultOther; if (!is_tabbing) @@ -9870,7 +10713,7 @@ static void ImGui::NavProcessItem() if (g.NavWindow != window) SetNavWindow(window); // Always refresh g.NavWindow, because some operations such as FocusItem() may not have a window. g.NavLayer = window->DC.NavLayerCurrent; - g.NavFocusScopeId = window->DC.NavFocusScopeIdCurrent; + g.NavFocusScopeId = g.CurrentFocusScopeId; g.NavIdIsAlive = true; window->NavRectRel[window->DC.NavLayerCurrent] = WindowRectAbsToRel(window, nav_bb); // Store item bounding box (relative to window position) } @@ -10058,7 +10901,8 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) if (window->Flags & ImGuiWindowFlags_NoNavInputs) { - g.NavId = g.NavFocusScopeId = 0; + g.NavId = 0; + g.NavFocusScopeId = window->NavRootFocusScopeId; return; } @@ -10068,7 +10912,7 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from NavInitWindow(), init_for_nav=%d, window=\"%s\", layer=%d\n", init_for_nav, window->Name, g.NavLayer); if (init_for_nav) { - SetNavID(0, g.NavLayer, 0, ImRect()); + SetNavID(0, g.NavLayer, window->NavRootFocusScopeId, ImRect()); g.NavInitRequest = true; g.NavInitRequestFromMove = false; g.NavInitResultId = 0; @@ -10078,7 +10922,7 @@ void ImGui::NavInitWindow(ImGuiWindow* window, bool force_reinit) else { g.NavId = window->NavLastIds[0]; - g.NavFocusScopeId = 0; + g.NavFocusScopeId = window->NavRootFocusScopeId; } } @@ -10110,56 +10954,27 @@ static ImVec2 ImGui::NavCalcPreferredRefPos() } } -const char* ImGui::GetNavInputName(ImGuiNavInput n) -{ - static const char* names[] = - { - "Activate", "Cancel", "Input", "Menu", "DpadLeft", "DpadRight", "DpadUp", "DpadDown", "LStickLeft", "LStickRight", "LStickUp", "LStickDown", - "FocusPrev", "FocusNext", "TweakSlow", "TweakFast", "KeyLeft", "KeyRight", "KeyUp", "KeyDown" - }; - IM_ASSERT(IM_ARRAYSIZE(names) == ImGuiNavInput_COUNT); - IM_ASSERT(n >= 0 && n < ImGuiNavInput_COUNT); - return names[n]; -} - -float ImGui::GetNavInputAmount(ImGuiNavInput n, ImGuiNavReadMode mode) +float ImGui::GetNavTweakPressedAmount(ImGuiAxis axis) { ImGuiContext& g = *GImGui; - if (mode == ImGuiNavReadMode_Down) - return g.IO.NavInputs[n]; // Instant, read analog input (0.0f..1.0f, as provided by user) + float repeat_delay, repeat_rate; + GetTypematicRepeatRate(ImGuiInputFlags_RepeatRateNavTweak, &repeat_delay, &repeat_rate); - const float t = g.IO.NavInputsDownDuration[n]; - if (t < 0.0f && mode == ImGuiNavReadMode_Released) // Return 1.0f when just released, no repeat, ignore analog input. - return (g.IO.NavInputsDownDurationPrev[n] >= 0.0f ? 1.0f : 0.0f); - if (t < 0.0f) - return 0.0f; - if (mode == ImGuiNavReadMode_Pressed) // Return 1.0f when just pressed, no repeat, ignore analog input. - return (t == 0.0f) ? 1.0f : 0.0f; - if (mode == ImGuiNavReadMode_Repeat) - return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.80f); - if (mode == ImGuiNavReadMode_RepeatSlow) - return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 1.25f, g.IO.KeyRepeatRate * 2.00f); - if (mode == ImGuiNavReadMode_RepeatFast) - return (float)CalcTypematicRepeatAmount(t - g.IO.DeltaTime, t, g.IO.KeyRepeatDelay * 0.72f, g.IO.KeyRepeatRate * 0.30f); - return 0.0f; -} - -ImVec2 ImGui::GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiNavReadMode mode, float slow_factor, float fast_factor) -{ - ImVec2 delta(0.0f, 0.0f); - if (dir_sources & ImGuiNavDirSourceFlags_RawKeyboard) - delta += ImVec2((float)IsKeyDown(ImGuiKey_RightArrow) - (float)IsKeyDown(ImGuiKey_LeftArrow), (float)IsKeyDown(ImGuiKey_DownArrow) - (float)IsKeyDown(ImGuiKey_UpArrow)); - if (dir_sources & ImGuiNavDirSourceFlags_Keyboard) - delta += ImVec2(GetNavInputAmount(ImGuiNavInput_KeyRight_, mode) - GetNavInputAmount(ImGuiNavInput_KeyLeft_, mode), GetNavInputAmount(ImGuiNavInput_KeyDown_, mode) - GetNavInputAmount(ImGuiNavInput_KeyUp_, mode)); - if (dir_sources & ImGuiNavDirSourceFlags_PadDPad) - delta += ImVec2(GetNavInputAmount(ImGuiNavInput_DpadRight, mode) - GetNavInputAmount(ImGuiNavInput_DpadLeft, mode), GetNavInputAmount(ImGuiNavInput_DpadDown, mode) - GetNavInputAmount(ImGuiNavInput_DpadUp, mode)); - if (dir_sources & ImGuiNavDirSourceFlags_PadLStick) - delta += ImVec2(GetNavInputAmount(ImGuiNavInput_LStickRight, mode) - GetNavInputAmount(ImGuiNavInput_LStickLeft, mode), GetNavInputAmount(ImGuiNavInput_LStickDown, mode) - GetNavInputAmount(ImGuiNavInput_LStickUp, mode)); - if (slow_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakSlow)) - delta *= slow_factor; - if (fast_factor != 0.0f && IsNavInputDown(ImGuiNavInput_TweakFast)) - delta *= fast_factor; - return delta; + ImGuiKey key_less, key_more; + if (g.NavInputSource == ImGuiInputSource_Gamepad) + { + key_less = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadLeft : ImGuiKey_GamepadDpadUp; + key_more = (axis == ImGuiAxis_X) ? ImGuiKey_GamepadDpadRight : ImGuiKey_GamepadDpadDown; + } + else + { + key_less = (axis == ImGuiAxis_X) ? ImGuiKey_LeftArrow : ImGuiKey_UpArrow; + key_more = (axis == ImGuiAxis_X) ? ImGuiKey_RightArrow : ImGuiKey_DownArrow; + } + float amount = (float)GetKeyPressedAmount(key_more, repeat_delay, repeat_rate) - (float)GetKeyPressedAmount(key_less, repeat_delay, repeat_rate); + if (amount != 0.0f && IsKeyDown(key_less) && IsKeyDown(key_more)) // Cancel when opposite directions are held, regardless of repeat phase + amount = 0.0f; + return amount; } static void ImGui::NavUpdate() @@ -10170,54 +10985,20 @@ static void ImGui::NavUpdate() io.WantSetMousePos = false; //if (g.NavScoringDebugCount > 0) IMGUI_DEBUG_LOG_NAV("[nav] NavScoringDebugCount %d for '%s' layer %d (Init:%d, Move:%d)\n", g.NavScoringDebugCount, g.NavWindow ? g.NavWindow->Name : "NULL", g.NavLayer, g.NavInitRequest || g.NavInitResultId != 0, g.NavMoveRequest); - // Update Gamepad->Nav inputs mapping - // Set input source as Gamepad when buttons are pressed (as some features differs when used with Gamepad vs Keyboard) + // Set input source based on which keys are last pressed (as some features differs when used with Gamepad vs Keyboard) + // FIXME-NAV: Now that keys are separated maybe we can get rid of NavInputSource? const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; - if (nav_gamepad_active && g.IO.BackendUsingLegacyNavInputArray == false) - { - for (int n = 0; n < ImGuiNavInput_COUNT; n++) - IM_ASSERT(io.NavInputs[n] == 0.0f && "Backend needs to either only use io.AddKeyEvent()/io.AddKeyAnalogEvent(), either only fill legacy io.NavInputs[]. Not both!"); - #define NAV_MAP_KEY(_KEY, _NAV_INPUT, _ACTIVATE_NAV) do { io.NavInputs[_NAV_INPUT] = io.KeysData[_KEY - ImGuiKey_KeysData_OFFSET].AnalogValue; if (_ACTIVATE_NAV && io.NavInputs[_NAV_INPUT] > 0.0f) { g.NavInputSource = ImGuiInputSource_Gamepad; } } while (0) - NAV_MAP_KEY(ImGuiKey_GamepadFaceDown, ImGuiNavInput_Activate, true); - NAV_MAP_KEY(ImGuiKey_GamepadFaceRight, ImGuiNavInput_Cancel, true); - NAV_MAP_KEY(ImGuiKey_GamepadFaceLeft, ImGuiNavInput_Menu, true); - NAV_MAP_KEY(ImGuiKey_GamepadFaceUp, ImGuiNavInput_Input, true); - NAV_MAP_KEY(ImGuiKey_GamepadDpadLeft, ImGuiNavInput_DpadLeft, true); - NAV_MAP_KEY(ImGuiKey_GamepadDpadRight, ImGuiNavInput_DpadRight, true); - NAV_MAP_KEY(ImGuiKey_GamepadDpadUp, ImGuiNavInput_DpadUp, true); - NAV_MAP_KEY(ImGuiKey_GamepadDpadDown, ImGuiNavInput_DpadDown, true); - NAV_MAP_KEY(ImGuiKey_GamepadL1, ImGuiNavInput_FocusPrev, false); - NAV_MAP_KEY(ImGuiKey_GamepadR1, ImGuiNavInput_FocusNext, false); - NAV_MAP_KEY(ImGuiKey_GamepadL1, ImGuiNavInput_TweakSlow, false); - NAV_MAP_KEY(ImGuiKey_GamepadR1, ImGuiNavInput_TweakFast, false); - NAV_MAP_KEY(ImGuiKey_GamepadLStickLeft, ImGuiNavInput_LStickLeft, false); - NAV_MAP_KEY(ImGuiKey_GamepadLStickRight, ImGuiNavInput_LStickRight, false); - NAV_MAP_KEY(ImGuiKey_GamepadLStickUp, ImGuiNavInput_LStickUp, false); - NAV_MAP_KEY(ImGuiKey_GamepadLStickDown, ImGuiNavInput_LStickDown, false); - #undef NAV_MAP_KEY - } - - // Update Keyboard->Nav inputs mapping + const ImGuiKey nav_gamepad_keys_to_change_source[] = { ImGuiKey_GamepadFaceRight, ImGuiKey_GamepadFaceLeft, ImGuiKey_GamepadFaceUp, ImGuiKey_GamepadFaceDown, ImGuiKey_GamepadDpadRight, ImGuiKey_GamepadDpadLeft, ImGuiKey_GamepadDpadUp, ImGuiKey_GamepadDpadDown }; + if (nav_gamepad_active) + for (ImGuiKey key : nav_gamepad_keys_to_change_source) + if (IsKeyDown(key)) + g.NavInputSource = ImGuiInputSource_Gamepad; const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; + const ImGuiKey nav_keyboard_keys_to_change_source[] = { ImGuiKey_Space, ImGuiKey_Enter, ImGuiKey_Escape, ImGuiKey_RightArrow, ImGuiKey_LeftArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow }; if (nav_keyboard_active) - { - #define NAV_MAP_KEY(_KEY, _NAV_INPUT) do { if (IsKeyDown(_KEY)) { io.NavInputs[_NAV_INPUT] = 1.0f; g.NavInputSource = ImGuiInputSource_Keyboard; } } while (0) - NAV_MAP_KEY(ImGuiKey_Space, ImGuiNavInput_Activate ); - NAV_MAP_KEY(ImGuiKey_Enter, ImGuiNavInput_Input ); - NAV_MAP_KEY(ImGuiKey_Escape, ImGuiNavInput_Cancel ); - NAV_MAP_KEY(ImGuiKey_LeftArrow, ImGuiNavInput_KeyLeft_ ); - NAV_MAP_KEY(ImGuiKey_RightArrow,ImGuiNavInput_KeyRight_); - NAV_MAP_KEY(ImGuiKey_UpArrow, ImGuiNavInput_KeyUp_ ); - NAV_MAP_KEY(ImGuiKey_DownArrow, ImGuiNavInput_KeyDown_ ); - if (io.KeyCtrl) - io.NavInputs[ImGuiNavInput_TweakSlow] = 1.0f; - if (io.KeyShift) - io.NavInputs[ImGuiNavInput_TweakFast] = 1.0f; - #undef NAV_MAP_KEY - } - memcpy(io.NavInputsDownDurationPrev, io.NavInputsDownDuration, sizeof(io.NavInputsDownDuration)); - for (int i = 0; i < IM_ARRAYSIZE(io.NavInputs); i++) - io.NavInputsDownDuration[i] = (io.NavInputs[i] > 0.0f) ? (io.NavInputsDownDuration[i] < 0.0f ? 0.0f : io.NavInputsDownDuration[i] + io.DeltaTime) : -1.0f; + for (ImGuiKey key : nav_keyboard_keys_to_change_source) + if (IsKeyDown(key)) + g.NavInputSource = ImGuiInputSource_Keyboard; // Process navigation init request (select first/default focus) if (g.NavInitResultId != 0) @@ -10262,10 +11043,10 @@ static void ImGui::NavUpdate() g.NavActivateFlags = ImGuiActivateFlags_None; if (g.NavId != 0 && !g.NavDisableHighlight && !g.NavWindowingTarget && g.NavWindow && !(g.NavWindow->Flags & ImGuiWindowFlags_NoNavInputs)) { - bool activate_down = IsNavInputDown(ImGuiNavInput_Activate); - bool input_down = IsNavInputDown(ImGuiNavInput_Input); - bool activate_pressed = activate_down && IsNavInputTest(ImGuiNavInput_Activate, ImGuiNavReadMode_Pressed); - bool input_pressed = input_down && IsNavInputTest(ImGuiNavInput_Input, ImGuiNavReadMode_Pressed); + const bool activate_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Space)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadActivate)); + const bool activate_pressed = activate_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Space, false)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadActivate, false))); + const bool input_down = (nav_keyboard_active && IsKeyDown(ImGuiKey_Enter)) || (nav_gamepad_active && IsKeyDown(ImGuiKey_NavGamepadInput)); + const bool input_pressed = input_down && ((nav_keyboard_active && IsKeyPressed(ImGuiKey_Enter, false)) || (nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadInput, false))); if (g.ActiveId == 0 && activate_pressed) { g.NavActivateId = g.NavId; @@ -10320,13 +11101,17 @@ static void ImGui::NavUpdate() SetScrollY(window, ImFloor(window->Scroll.y + ((move_dir == ImGuiDir_Up) ? -1.0f : +1.0f) * scroll_speed)); } - // *Normal* Manual scroll with NavScrollXXX keys + // *Normal* Manual scroll with LStick // Next movement request will clamp the NavId reference rectangle to the visible area, so navigation will resume within those bounds. - ImVec2 scroll_dir = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiNavReadMode_Down, 1.0f / 10.0f, 10.0f); - if (scroll_dir.x != 0.0f && window->ScrollbarX) - SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed)); - if (scroll_dir.y != 0.0f) - SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed)); + if (nav_gamepad_active) + { + const ImVec2 scroll_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown); + const float tweak_factor = IsKeyDown(ImGuiKey_NavGamepadTweakSlow) ? 1.0f / 10.0f : IsKeyDown(ImGuiKey_NavGamepadTweakFast) ? 10.0f : 1.0f; + if (scroll_dir.x != 0.0f && window->ScrollbarX) + SetScrollX(window, ImFloor(window->Scroll.x + scroll_dir.x * scroll_speed * tweak_factor)); + if (scroll_dir.y != 0.0f) + SetScrollY(window, ImFloor(window->Scroll.y + scroll_dir.y * scroll_speed * tweak_factor)); + } } // Always prioritize mouse highlight if navigation is disabled @@ -10378,6 +11163,8 @@ void ImGui::NavUpdateCreateMoveRequest() ImGuiContext& g = *GImGui; ImGuiIO& io = g.IO; ImGuiWindow* window = g.NavWindow; + const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; + const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; if (g.NavMoveForwardToNextFrame && window != NULL) { @@ -10395,11 +11182,11 @@ void ImGui::NavUpdateCreateMoveRequest() g.NavMoveScrollFlags = ImGuiScrollFlags_None; if (window && !g.NavWindowingTarget && !(window->Flags & ImGuiWindowFlags_NoNavInputs)) { - const ImGuiNavReadMode read_mode = ImGuiNavReadMode_Repeat; - if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && (IsNavInputTest(ImGuiNavInput_DpadLeft, read_mode) || IsNavInputTest(ImGuiNavInput_KeyLeft_, read_mode))) { g.NavMoveDir = ImGuiDir_Left; } - if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && (IsNavInputTest(ImGuiNavInput_DpadRight, read_mode) || IsNavInputTest(ImGuiNavInput_KeyRight_, read_mode))) { g.NavMoveDir = ImGuiDir_Right; } - if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && (IsNavInputTest(ImGuiNavInput_DpadUp, read_mode) || IsNavInputTest(ImGuiNavInput_KeyUp_, read_mode))) { g.NavMoveDir = ImGuiDir_Up; } - if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && (IsNavInputTest(ImGuiNavInput_DpadDown, read_mode) || IsNavInputTest(ImGuiNavInput_KeyDown_, read_mode))) { g.NavMoveDir = ImGuiDir_Down; } + const ImGuiInputFlags repeat_mode = ImGuiInputFlags_Repeat | (ImGuiInputFlags)ImGuiInputFlags_RepeatRateNavMove; + if (!IsActiveIdUsingNavDir(ImGuiDir_Left) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadLeft, ImGuiKeyOwner_None, repeat_mode)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_LeftArrow, ImGuiKeyOwner_None, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Left; } + if (!IsActiveIdUsingNavDir(ImGuiDir_Right) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadRight, ImGuiKeyOwner_None, repeat_mode)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_RightArrow, ImGuiKeyOwner_None, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Right; } + if (!IsActiveIdUsingNavDir(ImGuiDir_Up) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadUp, ImGuiKeyOwner_None, repeat_mode)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_UpArrow, ImGuiKeyOwner_None, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Up; } + if (!IsActiveIdUsingNavDir(ImGuiDir_Down) && ((nav_gamepad_active && IsKeyPressed(ImGuiKey_GamepadDpadDown, ImGuiKeyOwner_None, repeat_mode)) || (nav_keyboard_active && IsKeyPressed(ImGuiKey_DownArrow, ImGuiKeyOwner_None, repeat_mode)))) { g.NavMoveDir = ImGuiDir_Down; } } g.NavMoveClipDir = g.NavMoveDir; g.NavScoringNoClipRect = ImRect(+FLT_MAX, +FLT_MAX, -FLT_MAX, -FLT_MAX); @@ -10407,7 +11194,6 @@ void ImGui::NavUpdateCreateMoveRequest() // Update PageUp/PageDown/Home/End scroll // FIXME-NAV: Consider enabling those keys even without the master ImGuiConfigFlags_NavEnableKeyboard flag? - const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; float scoring_rect_offset_y = 0.0f; if (window && g.NavMoveDir == ImGuiDir_None && nav_keyboard_active) scoring_rect_offset_y = NavUpdatePageUpPageDown(); @@ -10433,7 +11219,7 @@ void ImGui::NavUpdateCreateMoveRequest() if (g.NavMoveDir != ImGuiDir_None) NavMoveRequestSubmit(g.NavMoveDir, g.NavMoveClipDir, g.NavMoveFlags, g.NavMoveScrollFlags); - // Moving with no reference triggers a init request (will be used as a fallback if the direction fails to find a match) + // Moving with no reference triggers an init request (will be used as a fallback if the direction fails to find a match) if (g.NavMoveSubmitted && g.NavId == 0) { IMGUI_DEBUG_LOG_NAV("[nav] NavInitRequest: from move, window \"%s\", layer=%d\n", window ? window->Name : "", g.NavLayer); @@ -10443,7 +11229,7 @@ void ImGui::NavUpdateCreateMoveRequest() } // When using gamepad, we project the reference nav bounding box into window visible area. - // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad every movements are relative + // This is to allow resuming navigation inside the visible area after doing a large amount of scrolling, since with gamepad all movements are relative // (can't focus a visible object like we can with the mouse). if (g.NavMoveSubmitted && g.NavInputSource == ImGuiInputSource_Gamepad && g.NavLayer == ImGuiNavLayer_Main && window != NULL)// && (g.NavMoveFlags & ImGuiNavMoveFlags_Forwarded)) { @@ -10460,7 +11246,7 @@ void ImGui::NavUpdateCreateMoveRequest() inner_rect_rel.Min.y = clamp_y ? (inner_rect_rel.Min.y + pad_y) : -FLT_MAX; inner_rect_rel.Max.y = clamp_y ? (inner_rect_rel.Max.y - pad_y) : +FLT_MAX; window->NavRectRel[g.NavLayer].ClipWithFull(inner_rect_rel); - g.NavId = g.NavFocusScopeId = 0; + g.NavId = 0; } } @@ -10473,7 +11259,7 @@ void ImGui::NavUpdateCreateMoveRequest() scoring_rect.TranslateY(scoring_rect_offset_y); scoring_rect.Min.x = ImMin(scoring_rect.Min.x + 1.0f, scoring_rect.Max.x); scoring_rect.Max.x = scoring_rect.Min.x; - IM_ASSERT(!scoring_rect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allows us to remove extraneous ImFabs() calls in NavScoreItem(). + IM_ASSERT(!scoring_rect.IsInverted()); // Ensure if we have a finite, non-inverted bounding box here will allow us to remove extraneous ImFabs() calls in NavScoreItem(). //GetForegroundDrawList()->AddRect(scoring_rect.Min, scoring_rect.Max, IM_COL32(255,200,0,255)); // [DEBUG] //if (!g.NavScoringNoClipRect.IsInverted()) { GetForegroundDrawList()->AddRect(g.NavScoringNoClipRect.Min, g.NavScoringNoClipRect.Max, IM_COL32(255, 200, 0, 255)); } // [DEBUG] } @@ -10489,7 +11275,7 @@ void ImGui::NavUpdateCreateTabbingRequest() if (window == NULL || g.NavWindowingTarget != NULL || (window->Flags & ImGuiWindowFlags_NoNavInputs)) return; - const bool tab_pressed = IsKeyPressed(ImGuiKey_Tab, true) && !IsActiveIdUsingKey(ImGuiKey_Tab) && !g.IO.KeyCtrl && !g.IO.KeyAlt; + const bool tab_pressed = IsKeyPressed(ImGuiKey_Tab, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat) && !g.IO.KeyCtrl && !g.IO.KeyAlt; if (!tab_pressed) return; @@ -10522,7 +11308,7 @@ void ImGui::NavMoveRequestApplyResult() if ((g.NavTabbingCounter == 1 || g.NavTabbingDir == 0) && g.NavTabbingResultFirst.ID) result = &g.NavTabbingResultFirst; - // In a situation when there is no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) + // In a situation when there are no results but NavId != 0, re-enable the Navigation highlight (because g.NavId is not considered as a possible result) if (result == NULL) { if (g.NavMoveFlags & ImGuiNavMoveFlags_Tabbing) @@ -10605,14 +11391,15 @@ void ImGui::NavMoveRequestApplyResult() static void ImGui::NavUpdateCancelRequest() { ImGuiContext& g = *GImGui; - if (!IsNavInputTest(ImGuiNavInput_Cancel, ImGuiNavReadMode_Pressed)) + const bool nav_gamepad_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (g.IO.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; + const bool nav_keyboard_active = (g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; + if (!(nav_keyboard_active && IsKeyPressed(ImGuiKey_Escape, ImGuiKeyOwner_None)) && !(nav_gamepad_active && IsKeyPressed(ImGuiKey_NavGamepadCancel, ImGuiKeyOwner_None))) return; - IMGUI_DEBUG_LOG_NAV("[nav] ImGuiNavInput_Cancel\n"); + IMGUI_DEBUG_LOG_NAV("[nav] NavUpdateCancelRequest()\n"); if (g.ActiveId != 0) { - if (!IsActiveIdUsingNavInput(ImGuiNavInput_Cancel)) - ClearActiveID(); + ClearActiveID(); } else if (g.NavLayer != ImGuiNavLayer_Main) { @@ -10631,7 +11418,7 @@ static void ImGui::NavUpdateCancelRequest() SetNavID(child_window->ChildId, ImGuiNavLayer_Main, 0, WindowRectAbsToRel(parent_window, child_rect)); NavRestoreHighlightAfterMove(); } - else if (g.OpenPopupStack.Size > 0 && !(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) + else if (g.OpenPopupStack.Size > 0 && g.OpenPopupStack.back().Window != NULL && !(g.OpenPopupStack.back().Window->Flags & ImGuiWindowFlags_Modal)) { // Close open popup/menu ClosePopupToLevel(g.OpenPopupStack.Size - 1, true); @@ -10641,7 +11428,7 @@ static void ImGui::NavUpdateCancelRequest() // Clear NavLastId for popups but keep it for regular child window so we can leave one and come back where we were if (g.NavWindow && ((g.NavWindow->Flags & ImGuiWindowFlags_Popup) || !(g.NavWindow->Flags & ImGuiWindowFlags_ChildWindow))) g.NavWindow->NavLastIds[0] = 0; - g.NavId = g.NavFocusScopeId = 0; + g.NavId = 0; } } @@ -10656,10 +11443,10 @@ static float ImGui::NavUpdatePageUpPageDown() if ((window->Flags & ImGuiWindowFlags_NoNavInputs) || g.NavWindowingTarget != NULL) return 0.0f; - const bool page_up_held = IsKeyDown(ImGuiKey_PageUp) && !IsActiveIdUsingKey(ImGuiKey_PageUp); - const bool page_down_held = IsKeyDown(ImGuiKey_PageDown) && !IsActiveIdUsingKey(ImGuiKey_PageDown); - const bool home_pressed = IsKeyPressed(ImGuiKey_Home) && !IsActiveIdUsingKey(ImGuiKey_Home); - const bool end_pressed = IsKeyPressed(ImGuiKey_End) && !IsActiveIdUsingKey(ImGuiKey_End); + const bool page_up_held = IsKeyDown(ImGuiKey_PageUp, ImGuiKeyOwner_None); + const bool page_down_held = IsKeyDown(ImGuiKey_PageDown, ImGuiKeyOwner_None); + const bool home_pressed = IsKeyPressed(ImGuiKey_Home, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat); + const bool end_pressed = IsKeyPressed(ImGuiKey_End, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat); if (page_up_held == page_down_held && home_pressed == end_pressed) // Proceed if either (not both) are pressed, otherwise early out return 0.0f; @@ -10669,9 +11456,9 @@ static float ImGui::NavUpdatePageUpPageDown() if (window->DC.NavLayersActiveMask == 0x00 && window->DC.NavHasScroll) { // Fallback manual-scroll when window has no navigable item - if (IsKeyPressed(ImGuiKey_PageUp, true)) + if (IsKeyPressed(ImGuiKey_PageUp, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat)) SetScrollY(window, window->Scroll.y - window->InnerRect.GetHeight()); - else if (IsKeyPressed(ImGuiKey_PageDown, true)) + else if (IsKeyPressed(ImGuiKey_PageDown, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat)) SetScrollY(window, window->Scroll.y + window->InnerRect.GetHeight()); else if (home_pressed) SetScrollY(window, 0.0f); @@ -10825,7 +11612,10 @@ static void NavUpdateWindowingHighlightWindow(int focus_change_dir) if (!window_target) window_target = FindWindowNavFocusable((focus_change_dir < 0) ? (g.WindowsFocusOrder.Size - 1) : 0, i_current, focus_change_dir); if (window_target) // Don't reset windowing target if there's a single window in the list + { g.NavWindowingTarget = g.NavWindowingTargetAnim = window_target; + g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f); + } g.NavWindowingToggleLayer = false; } @@ -10854,13 +11644,18 @@ static void ImGui::NavUpdateWindowing() } // Start CTRL+Tab or Square+L/R window selection - const bool start_windowing_with_gamepad = allow_windowing && !g.NavWindowingTarget && IsNavInputTest(ImGuiNavInput_Menu, ImGuiNavReadMode_Pressed); - const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && io.KeyCtrl && IsKeyPressed(ImGuiKey_Tab); + const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; + const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; + const bool keyboard_next_window = allow_windowing && g.ConfigNavWindowingKeyNext && Shortcut(g.ConfigNavWindowingKeyNext, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways); + const bool keyboard_prev_window = allow_windowing && g.ConfigNavWindowingKeyPrev && Shortcut(g.ConfigNavWindowingKeyPrev, ImGuiKeyOwner_None, ImGuiInputFlags_Repeat | ImGuiInputFlags_RouteAlways); + const bool start_windowing_with_gamepad = allow_windowing && nav_gamepad_active && !g.NavWindowingTarget && IsKeyPressed(ImGuiKey_NavGamepadMenu, 0, ImGuiInputFlags_None); + const bool start_windowing_with_keyboard = allow_windowing && !g.NavWindowingTarget && (keyboard_next_window || keyboard_prev_window); // Note: enabled even without NavEnableKeyboard! if (start_windowing_with_gamepad || start_windowing_with_keyboard) if (ImGuiWindow* window = g.NavWindow ? g.NavWindow : FindWindowNavFocusable(g.WindowsFocusOrder.Size - 1, -INT_MAX, -1)) { g.NavWindowingTarget = g.NavWindowingTargetAnim = window->RootWindow; g.NavWindowingTimer = g.NavWindowingHighlightAlpha = 0.0f; + g.NavWindowingAccumDeltaPos = g.NavWindowingAccumDeltaSize = ImVec2(0.0f, 0.0f); g.NavWindowingToggleLayer = start_windowing_with_gamepad ? true : false; // Gamepad starts toggling layer g.NavInputSource = start_windowing_with_keyboard ? ImGuiInputSource_Keyboard : ImGuiInputSource_Gamepad; } @@ -10873,7 +11668,7 @@ static void ImGui::NavUpdateWindowing() g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // Select window to focus - const int focus_change_dir = (int)IsNavInputTest(ImGuiNavInput_FocusPrev, ImGuiNavReadMode_RepeatSlow) - (int)IsNavInputTest(ImGuiNavInput_FocusNext, ImGuiNavReadMode_RepeatSlow); + const int focus_change_dir = (int)IsKeyPressed(ImGuiKey_GamepadL1) - (int)IsKeyPressed(ImGuiKey_GamepadR1); if (focus_change_dir != 0) { NavUpdateWindowingHighlightWindow(focus_change_dir); @@ -10881,7 +11676,7 @@ static void ImGui::NavUpdateWindowing() } // Single press toggles NavLayer, long press with L/R apply actual focus on release (until then the window was merely rendered top-most) - if (!IsNavInputDown(ImGuiNavInput_Menu)) + if (!IsKeyDown(ImGuiKey_NavGamepadMenu)) { g.NavWindowingToggleLayer &= (g.NavWindowingHighlightAlpha < 1.0f); // Once button was held long enough we don't consider it a tap-to-toggle-layer press anymore. if (g.NavWindowingToggleLayer && g.NavWindow) @@ -10896,18 +11691,19 @@ static void ImGui::NavUpdateWindowing() if (g.NavWindowingTarget && g.NavInputSource == ImGuiInputSource_Keyboard) { // Visuals only appears after a brief time after pressing TAB the first time, so that a fast CTRL+TAB doesn't add visual noise + ImGuiKeyChord shared_mods = ((g.ConfigNavWindowingKeyNext ? g.ConfigNavWindowingKeyNext : ImGuiMod_Mask_) & (g.ConfigNavWindowingKeyPrev ? g.ConfigNavWindowingKeyPrev : ImGuiMod_Mask_)) & ImGuiMod_Mask_; + IM_ASSERT(shared_mods != 0); // Next/Prev shortcut currently needs a shared modifier to "hold", otherwise Prev actions would keep cycling between two windows. g.NavWindowingHighlightAlpha = ImMax(g.NavWindowingHighlightAlpha, ImSaturate((g.NavWindowingTimer - NAV_WINDOWING_HIGHLIGHT_DELAY) / 0.05f)); // 1.0f - if (IsKeyPressed(ImGuiKey_Tab, true)) - NavUpdateWindowingHighlightWindow(io.KeyShift ? +1 : -1); - if (!io.KeyCtrl) + if (keyboard_next_window || keyboard_prev_window) + NavUpdateWindowingHighlightWindow(keyboard_next_window ? -1 : +1); + else if ((io.KeyMods & shared_mods) != shared_mods) apply_focus_window = g.NavWindowingTarget; } // Keyboard: Press and Release ALT to toggle menu layer // - Testing that only Alt is tested prevents Alt+Shift or AltGR from toggling menu layer. // - AltGR is normally Alt+Ctrl but we can't reliably detect it (not all backends/systems/layout emit it as Alt+Ctrl). But even on keyboards without AltGR we don't want Alt+Ctrl to open menu anyway. - const bool nav_keyboard_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableKeyboard) != 0; - if (nav_keyboard_active && IsKeyPressed(ImGuiKey_ModAlt)) + if (nav_keyboard_active && IsKeyPressed(ImGuiMod_Alt, ImGuiKeyOwner_None)) { g.NavWindowingToggleLayer = true; g.NavInputSource = ImGuiInputSource_Keyboard; @@ -10916,34 +11712,41 @@ static void ImGui::NavUpdateWindowing() { // We cancel toggling nav layer when any text has been typed (generally while holding Alt). (See #370) // We cancel toggling nav layer when other modifiers are pressed. (See #4439) - if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper) + // We cancel toggling nav layer if an owner has claimed the key. + if (io.InputQueueCharacters.Size > 0 || io.KeyCtrl || io.KeyShift || io.KeySuper || TestKeyOwner(ImGuiMod_Alt, ImGuiKeyOwner_None) == false) g.NavWindowingToggleLayer = false; // Apply layer toggle on release // Important: as before version <18314 we lacked an explicit IO event for focus gain/loss, we also compare mouse validity to detect old backends clearing mouse pos on focus loss. - if (IsKeyReleased(ImGuiKey_ModAlt) && g.NavWindowingToggleLayer) + if (IsKeyReleased(ImGuiMod_Alt) && g.NavWindowingToggleLayer) if (g.ActiveId == 0 || g.ActiveIdAllowOverlap) if (IsMousePosValid(&io.MousePos) == IsMousePosValid(&io.MousePosPrev)) apply_toggle_layer = true; - if (!IsKeyDown(ImGuiKey_ModAlt)) + if (!IsKeyDown(ImGuiMod_Alt)) g.NavWindowingToggleLayer = false; } // Move window if (g.NavWindowingTarget && !(g.NavWindowingTarget->Flags & ImGuiWindowFlags_NoMove)) { - ImVec2 move_delta; + ImVec2 nav_move_dir; if (g.NavInputSource == ImGuiInputSource_Keyboard && !io.KeyShift) - move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_RawKeyboard, ImGuiNavReadMode_Down); + nav_move_dir = GetKeyMagnitude2d(ImGuiKey_LeftArrow, ImGuiKey_RightArrow, ImGuiKey_UpArrow, ImGuiKey_DownArrow); if (g.NavInputSource == ImGuiInputSource_Gamepad) - move_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_PadLStick, ImGuiNavReadMode_Down); - if (move_delta.x != 0.0f || move_delta.y != 0.0f) + nav_move_dir = GetKeyMagnitude2d(ImGuiKey_GamepadLStickLeft, ImGuiKey_GamepadLStickRight, ImGuiKey_GamepadLStickUp, ImGuiKey_GamepadLStickDown); + if (nav_move_dir.x != 0.0f || nav_move_dir.y != 0.0f) { const float NAV_MOVE_SPEED = 800.0f; - const float move_speed = ImFloor(NAV_MOVE_SPEED * io.DeltaTime * ImMin(io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y)); // FIXME: Doesn't handle variable framerate very well - ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow; - SetWindowPos(moving_window, moving_window->Pos + move_delta * move_speed, ImGuiCond_Always); + const float move_step = NAV_MOVE_SPEED * io.DeltaTime * ImMin(io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y); + g.NavWindowingAccumDeltaPos += nav_move_dir * move_step; g.NavDisableMouseHover = true; + ImVec2 accum_floored = ImFloor(g.NavWindowingAccumDeltaPos); + if (accum_floored.x != 0.0f || accum_floored.y != 0.0f) + { + ImGuiWindow* moving_window = g.NavWindowingTarget->RootWindow; + SetWindowPos(moving_window, moving_window->Pos + accum_floored, ImGuiCond_Always); + g.NavWindowingAccumDeltaPos -= accum_floored; + } } } @@ -11007,10 +11810,10 @@ static void ImGui::NavUpdateWindowing() static const char* GetFallbackWindowNameForWindowingList(ImGuiWindow* window) { if (window->Flags & ImGuiWindowFlags_Popup) - return "(Popup)"; + return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingPopup); if ((window->Flags & ImGuiWindowFlags_MenuBar) && strcmp(window->Name, "##MainMenuBar") == 0) - return "(Main menu bar)"; - return "(Untitled)"; + return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingMainMenuBar); + return ImGui::LocalizeGetMsg(ImGuiLocKey_WindowingUntitled); } // Overlay displayed when using CTRL+TAB. Called by EndFrame(). @@ -11140,7 +11943,7 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags) source_drag_active = IsMouseDragging(mouse_button); // Disable navigation and key inputs while dragging + cancel existing request if any - SetActiveIdUsingNavAndKeys(); + SetActiveIdUsingAllKeyboardKeys(); } else { @@ -11272,7 +12075,7 @@ bool ImGui::BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id) } // We don't use BeginDragDropTargetCustom() and duplicate its code because: -// 1) we use LastItemRectHoveredRect which handles items that pushes a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them. +// 1) we use LastItemRectHoveredRect which handles items that push a temporarily clip rectangle in their code. Calling BeginDragDropTargetCustom(LastItemRect) would not handle them. // 2) and it's faster. as this code may be very frequently called, we want to early out as fast as we can. // Also note how the HoveredWindow test is positioned differently in both functions (in both functions we optimize for the cheapest early out case) bool ImGui::BeginDragDropTarget() @@ -11334,9 +12137,8 @@ const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDrop } // Render default drop visuals - // FIXME-DRAGDROP: Settle on a proper default visuals for drop target. payload.Preview = was_accepted_previously; - flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that lives for 1 frame) + flags |= (g.DragDropSourceFlags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect); // Source can also inhibit the preview (useful for external sources that live for 1 frame) if (!(flags & ImGuiDragDropFlags_AcceptNoDrawDefaultRect) && payload.Preview) window->DrawList->AddRect(r.Min - ImVec2(3.5f,3.5f), r.Max + ImVec2(3.5f, 3.5f), GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f); @@ -11348,10 +12150,16 @@ const ImGuiPayload* ImGui::AcceptDragDropPayload(const char* type, ImGuiDragDrop return &payload; } +// FIXME-DRAGDROP: Settle on a proper default visuals for drop target. +void ImGui::RenderDragDropTargetRect(const ImRect& bb) +{ + GetWindowDrawList()->AddRect(bb.Min - ImVec2(3.5f, 3.5f), bb.Max + ImVec2(3.5f, 3.5f), GetColorU32(ImGuiCol_DragDropTarget), 0.0f, 0, 2.0f); +} + const ImGuiPayload* ImGui::GetDragDropPayload() { ImGuiContext& g = *GImGui; - return g.DragDropActive ? &g.DragDropPayload : NULL; + return (g.DragDropActive && g.DragDropPayload.DataFrameCount != -1) ? &g.DragDropPayload : NULL; } // We don't really use/need this now, but added it for the sake of consistency and because we might need it later. @@ -11935,6 +12743,18 @@ static void WindowSettingsHandler_WriteAll(ImGuiContext* ctx, ImGuiSettingsHandl } +//----------------------------------------------------------------------------- +// [SECTION] LOCALIZATION +//----------------------------------------------------------------------------- + +void ImGui::LocalizeRegisterEntries(const ImGuiLocEntry* entries, int count) +{ + ImGuiContext& g = *GImGui; + for (int n = 0; n < count; n++) + g.LocalizationTable[entries[n].Key] = entries[n].Text; +} + + //----------------------------------------------------------------------------- // [SECTION] VIEWPORTS, PLATFORM WINDOWS //----------------------------------------------------------------------------- @@ -12136,8 +12956,7 @@ static void SetPlatformImeDataFn_DefaultImpl(ImGuiViewport* viewport, ImGuiPlatf if (hwnd == 0) return; - ::ImmAssociateContextEx(hwnd, NULL, data->WantVisible ? IACE_DEFAULT : 0); - + //::ImmAssociateContextEx(hwnd, NULL, data->WantVisible ? IACE_DEFAULT : 0); if (HIMC himc = ::ImmGetContext(hwnd)) { COMPOSITIONFORM composition_form = {}; @@ -12236,6 +13055,55 @@ static void RenderViewportsThumbnails() ImGui::Dummy(bb_full.GetSize() * SCALE); } +// Draw an arbitrary US keyboard layout to visualize translated keys +void ImGui::DebugRenderKeyboardPreview(ImDrawList* draw_list) +{ + const ImVec2 key_size = ImVec2(35.0f, 35.0f); + const float key_rounding = 3.0f; + const ImVec2 key_face_size = ImVec2(25.0f, 25.0f); + const ImVec2 key_face_pos = ImVec2(5.0f, 3.0f); + const float key_face_rounding = 2.0f; + const ImVec2 key_label_pos = ImVec2(7.0f, 4.0f); + const ImVec2 key_step = ImVec2(key_size.x - 1.0f, key_size.y - 1.0f); + const float key_row_offset = 9.0f; + + ImVec2 board_min = GetCursorScreenPos(); + ImVec2 board_max = ImVec2(board_min.x + 3 * key_step.x + 2 * key_row_offset + 10.0f, board_min.y + 3 * key_step.y + 10.0f); + ImVec2 start_pos = ImVec2(board_min.x + 5.0f - key_step.x, board_min.y); + + struct KeyLayoutData { int Row, Col; const char* Label; ImGuiKey Key; }; + const KeyLayoutData keys_to_display[] = + { + { 0, 0, "", ImGuiKey_Tab }, { 0, 1, "Q", ImGuiKey_Q }, { 0, 2, "W", ImGuiKey_W }, { 0, 3, "E", ImGuiKey_E }, { 0, 4, "R", ImGuiKey_R }, + { 1, 0, "", ImGuiKey_CapsLock }, { 1, 1, "A", ImGuiKey_A }, { 1, 2, "S", ImGuiKey_S }, { 1, 3, "D", ImGuiKey_D }, { 1, 4, "F", ImGuiKey_F }, + { 2, 0, "", ImGuiKey_LeftShift },{ 2, 1, "Z", ImGuiKey_Z }, { 2, 2, "X", ImGuiKey_X }, { 2, 3, "C", ImGuiKey_C }, { 2, 4, "V", ImGuiKey_V } + }; + + // Elements rendered manually via ImDrawList API are not clipped automatically. + // While not strictly necessary, here IsItemVisible() is used to avoid rendering these shapes when they are out of view. + Dummy(board_max - board_min); + if (!IsItemVisible()) + return; + draw_list->PushClipRect(board_min, board_max, true); + for (int n = 0; n < IM_ARRAYSIZE(keys_to_display); n++) + { + const KeyLayoutData* key_data = &keys_to_display[n]; + ImVec2 key_min = ImVec2(start_pos.x + key_data->Col * key_step.x + key_data->Row * key_row_offset, start_pos.y + key_data->Row * key_step.y); + ImVec2 key_max = key_min + key_size; + draw_list->AddRectFilled(key_min, key_max, IM_COL32(204, 204, 204, 255), key_rounding); + draw_list->AddRect(key_min, key_max, IM_COL32(24, 24, 24, 255), key_rounding); + ImVec2 face_min = ImVec2(key_min.x + key_face_pos.x, key_min.y + key_face_pos.y); + ImVec2 face_max = ImVec2(face_min.x + key_face_size.x, face_min.y + key_face_size.y); + draw_list->AddRect(face_min, face_max, IM_COL32(193, 193, 193, 255), key_face_rounding, ImDrawFlags_None, 2.0f); + draw_list->AddRectFilled(face_min, face_max, IM_COL32(252, 252, 252, 255), key_face_rounding); + ImVec2 label_min = ImVec2(key_min.x + key_label_pos.x, key_min.y + key_label_pos.y); + draw_list->AddText(label_min, IM_COL32(64, 64, 64, 255), key_data->Label); + if (ImGui::IsKeyDown(key_data->Key)) + draw_list->AddRectFilled(key_min, key_max, IM_COL32(255, 0, 0, 128), key_rounding); + } + draw_list->PopClipRect(); +} + // Helper tool to diagnose between text encoding issues and font loading issues. Pass your UTF-8 string and verify that there are correct. void ImGui::DebugTextEncoding(const char* str) { @@ -12276,7 +13144,7 @@ void ImGui::DebugTextEncoding(const char* str) static void MetricsHelpMarker(const char* desc) { ImGui::TextDisabled("(?)"); - if (ImGui::IsItemHovered()) + if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort)) { ImGui::BeginTooltip(); ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); @@ -12356,8 +13224,8 @@ void ImGui::ShowMetricsWindow(bool* p_open) else if (rect_type == TRT_ColumnsClipRect) { ImGuiTableColumn* c = &table->Columns[n]; return c->ClipRect; } else if (rect_type == TRT_ColumnsContentHeadersUsed){ ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersUsed, table->InnerClipRect.Min.y + table_instance->LastFirstRowHeight); } // Note: y1/y2 not always accurate else if (rect_type == TRT_ColumnsContentHeadersIdeal){ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXHeadersIdeal, table->InnerClipRect.Min.y + table_instance->LastFirstRowHeight); } - else if (rect_type == TRT_ColumnsContentFrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXFrozen, table->InnerClipRect.Min.y + table_instance->LastFirstRowHeight); } - else if (rect_type == TRT_ColumnsContentUnfrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y + table_instance->LastFirstRowHeight, c->ContentMaxXUnfrozen, table->InnerClipRect.Max.y); } + else if (rect_type == TRT_ColumnsContentFrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y, c->ContentMaxXFrozen, table->InnerClipRect.Min.y + table_instance->LastFrozenHeight); } + else if (rect_type == TRT_ColumnsContentUnfrozen) { ImGuiTableColumn* c = &table->Columns[n]; return ImRect(c->WorkMinX, table->InnerClipRect.Min.y + table_instance->LastFrozenHeight, c->ContentMaxXUnfrozen, table->InnerClipRect.Max.y); } IM_ASSERT(0); return ImRect(); } @@ -12531,8 +13399,12 @@ void ImGui::ShowMetricsWindow(bool* p_open) { for (int i = 0; i < g.OpenPopupStack.Size; i++) { - ImGuiWindow* window = g.OpenPopupStack[i].Window; - BulletText("PopupID: %08x, Window: '%s'%s%s", g.OpenPopupStack[i].PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? " ChildWindow" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? " ChildMenu" : ""); + // As it's difficult to interact with tree nodes while popups are open, we display everything inline. + const ImGuiPopupData* popup_data = &g.OpenPopupStack[i]; + ImGuiWindow* window = popup_data->Window; + BulletText("PopupID: %08x, Window: '%s' (%s%s), BackupNavWindow '%s', ParentWindow '%s'", + popup_data->PopupId, window ? window->Name : "NULL", window && (window->Flags & ImGuiWindowFlags_ChildWindow) ? "Child;" : "", window && (window->Flags & ImGuiWindowFlags_ChildMenu) ? "Menu;" : "", + popup_data->BackupNavWindow ? popup_data->BackupNavWindow->Name : "NULL", window && window->ParentWindow ? window->ParentWindow->Name : "NULL"); } TreePop(); } @@ -12630,7 +13502,98 @@ void ImGui::ShowMetricsWindow(bool* p_open) TreePop(); } - // Misc Details + if (TreeNode("Inputs")) + { + Text("KEYBOARD/GAMEPAD/MOUSE KEYS"); + { + // We iterate both legacy native range and named ImGuiKey ranges, which is a little odd but this allows displaying the data for old/new backends. + // User code should never have to go through such hoops: old code may use native keycodes, new code may use ImGuiKey codes. + Indent(); +#ifdef IMGUI_DISABLE_OBSOLETE_KEYIO + struct funcs { static bool IsLegacyNativeDupe(ImGuiKey) { return false; } }; +#else + struct funcs { static bool IsLegacyNativeDupe(ImGuiKey key) { return key < 512 && GetIO().KeyMap[key] != -1; } }; // Hide Native<>ImGuiKey duplicates when both exists in the array + //Text("Legacy raw:"); for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key++) { if (io.KeysDown[key]) { SameLine(); Text("\"%s\" %d", GetKeyName(key), key); } } +#endif + Text("Keys down:"); for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !IsKeyDown(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); SameLine(); Text("(%.02f)", GetKeyData(key)->DownDuration); } + Text("Keys pressed:"); for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !IsKeyPressed(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); } + Text("Keys released:"); for (ImGuiKey key = ImGuiKey_KeysData_OFFSET; key < ImGuiKey_COUNT; key = (ImGuiKey)(key + 1)) { if (funcs::IsLegacyNativeDupe(key) || !IsKeyReleased(key)) continue; SameLine(); Text(IsNamedKey(key) ? "\"%s\"" : "\"%s\" %d", GetKeyName(key), key); } + Text("Keys mods: %s%s%s%s", io.KeyCtrl ? "CTRL " : "", io.KeyShift ? "SHIFT " : "", io.KeyAlt ? "ALT " : "", io.KeySuper ? "SUPER " : ""); + Text("Chars queue:"); for (int i = 0; i < io.InputQueueCharacters.Size; i++) { ImWchar c = io.InputQueueCharacters[i]; SameLine(); Text("\'%c\' (0x%04X)", (c > ' ' && c <= 255) ? (char)c : '?', c); } // FIXME: We should convert 'c' to UTF-8 here but the functions are not public. + DebugRenderKeyboardPreview(GetWindowDrawList()); + Unindent(); + } + + Text("MOUSE STATE"); + { + Indent(); + if (IsMousePosValid()) + Text("Mouse pos: (%g, %g)", io.MousePos.x, io.MousePos.y); + else + Text("Mouse pos: "); + Text("Mouse delta: (%g, %g)", io.MouseDelta.x, io.MouseDelta.y); + int count = IM_ARRAYSIZE(io.MouseDown); + Text("Mouse down:"); for (int i = 0; i < count; i++) if (IsMouseDown(i)) { SameLine(); Text("b%d (%.02f secs)", i, io.MouseDownDuration[i]); } + Text("Mouse clicked:"); for (int i = 0; i < count; i++) if (IsMouseClicked(i)) { SameLine(); Text("b%d (%d)", i, io.MouseClickedCount[i]); } + Text("Mouse released:"); for (int i = 0; i < count; i++) if (IsMouseReleased(i)) { SameLine(); Text("b%d", i); } + Text("Mouse wheel: %.1f", io.MouseWheel); + Text("Pen Pressure: %.1f", io.PenPressure); // Note: currently unused + Unindent(); + } + + Text("MOUSE WHEELING"); + { + Indent(); + Text("WheelingWindow: '%s'", g.WheelingWindow ? g.WheelingWindow->Name : "NULL"); + Text("WheelingWindowReleaseTimer: %.2f", g.WheelingWindowReleaseTimer); + Text("WheelingAxisAvg[] = { %.3f, %.3f }, Main Axis: %s", g.WheelingAxisAvg.x, g.WheelingAxisAvg.y, (g.WheelingAxisAvg.x > g.WheelingAxisAvg.y) ? "X" : (g.WheelingAxisAvg.x < g.WheelingAxisAvg.y) ? "Y" : ""); + Unindent(); + } + + Text("KEY OWNERS"); + { + Indent(); + if (BeginListBox("##owners", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 6))) + { + for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) + { + ImGuiKeyOwnerData* owner_data = GetKeyOwnerData(key); + if (owner_data->OwnerCurr == ImGuiKeyOwner_None) + continue; + Text("%s: 0x%08X%s", GetKeyName(key), owner_data->OwnerCurr, + owner_data->LockUntilRelease ? " LockUntilRelease" : owner_data->LockThisFrame ? " LockThisFrame" : ""); + DebugLocateItemOnHover(owner_data->OwnerCurr); + } + EndListBox(); + } + Unindent(); + } + Text("SHORTCUT ROUTING"); + { + Indent(); + if (BeginListBox("##routes", ImVec2(-FLT_MIN, GetTextLineHeightWithSpacing() * 6))) + { + for (ImGuiKey key = ImGuiKey_NamedKey_BEGIN; key < ImGuiKey_NamedKey_END; key = (ImGuiKey)(key + 1)) + { + ImGuiKeyRoutingTable* rt = &g.KeysRoutingTable; + for (ImGuiKeyRoutingIndex idx = rt->Index[key - ImGuiKey_NamedKey_BEGIN]; idx != -1; ) + { + char key_chord_name[64]; + ImGuiKeyRoutingData* routing_data = &rt->Entries[idx]; + GetKeyChordName(key | routing_data->Mods, key_chord_name, IM_ARRAYSIZE(key_chord_name)); + Text("%s: 0x%08X", key_chord_name, routing_data->RoutingCurr); + DebugLocateItemOnHover(routing_data->RoutingCurr); + idx = routing_data->NextEntryIndex; + } + } + EndListBox(); + } + Text("(ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: 0x%X)", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask); + Unindent(); + } + TreePop(); + } + if (TreeNode("Internal state")) { Text("WINDOWING"); @@ -12644,20 +13607,20 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("ITEMS"); Indent(); Text("ActiveId: 0x%08X/0x%08X (%.2f sec), AllowOverlap: %d, Source: %s", g.ActiveId, g.ActiveIdPreviousFrame, g.ActiveIdTimer, g.ActiveIdAllowOverlap, GetInputSourceName(g.ActiveIdSource)); + DebugLocateItemOnHover(g.ActiveId); Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); - - int active_id_using_key_input_count = 0; - for (int n = ImGuiKey_NamedKey_BEGIN; n < ImGuiKey_NamedKey_END; n++) - active_id_using_key_input_count += g.ActiveIdUsingKeyInputMask[n] ? 1 : 0; - Text("ActiveIdUsing: Wheel: %d, NavDirMask: %X, NavInputMask: %X, KeyInputMask: %d key(s)", g.ActiveIdUsingMouseWheel, g.ActiveIdUsingNavDirMask, g.ActiveIdUsingNavInputMask, active_id_using_key_input_count); + Text("ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: %X", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask); Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame + Text("HoverDelayId: 0x%08X, Timer: %.2f, ClearTimer: %.2f", g.HoverDelayId, g.HoverDelayTimer, g.HoverDelayClearTimer); Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); + DebugLocateItemOnHover(g.DragDropPayload.SourceId); Unindent(); Text("NAV,FOCUS"); Indent(); Text("NavWindow: '%s'", g.NavWindow ? g.NavWindow->Name : "NULL"); Text("NavId: 0x%08X, NavLayer: %d", g.NavId, g.NavLayer); + DebugLocateItemOnHover(g.NavId); Text("NavInputSource: %s", GetInputSourceName(g.NavInputSource)); Text("NavActive: %d, NavVisible: %d", g.IO.NavActive, g.IO.NavVisible); Text("NavActivateId/DownId/PressedId/InputId: %08X/%08X/%08X/%08X", g.NavActivateId, g.NavActivateDownId, g.NavActivatePressedId, g.NavActivateInputId); @@ -13017,7 +13980,7 @@ void ImGui::DebugNodeTabBar(ImGuiTabBar* tab_bar, const char* label) PushID(tab); if (SmallButton("<")) { TabBarQueueReorder(tab_bar, tab, -1); } SameLine(0, 2); if (SmallButton(">")) { TabBarQueueReorder(tab_bar, tab, +1); } SameLine(); - Text("%02d%c Tab 0x%08X '%s' Offset: %.1f, Width: %.1f/%.1f", + Text("%02d%c Tab 0x%08X '%s' Offset: %.2f, Width: %.2f/%.2f", tab_n, (tab->ID == tab_bar->SelectedTabId) ? '*' : ' ', tab->ID, (tab->NameOffset != -1) ? tab_bar->GetTabName(tab) : "???", tab->Offset, tab->Width, tab->ContentWidth); PopID(); } @@ -13081,13 +14044,10 @@ void ImGui::DebugNodeWindow(ImGuiWindow* window, const char* label) { ImRect r = window->NavRectRel[layer]; if (r.Min.x >= r.Max.y && r.Min.y >= r.Max.y) - { BulletText("NavLastIds[%d]: 0x%08X", layer, window->NavLastIds[layer]); - continue; - } - BulletText("NavLastIds[%d]: 0x%08X at +(%.1f,%.1f)(%.1f,%.1f)", layer, window->NavLastIds[layer], r.Min.x, r.Min.y, r.Max.x, r.Max.y); - if (IsItemHovered()) - GetForegroundDrawList(window)->AddRect(r.Min + window->Pos, r.Max + window->Pos, IM_COL32(255, 255, 0, 255)); + else + BulletText("NavLastIds[%d]: 0x%08X at +(%.1f,%.1f)(%.1f,%.1f)", layer, window->NavLastIds[layer], r.Min.x, r.Min.y, r.Max.x, r.Max.y); + DebugLocateItemOnHover(window->NavLastIds[layer]); } BulletText("NavLayersActiveMask: %X, NavLastChildNavWindow: %s", window->DC.NavLayersActiveMask, window->NavLastChildNavWindow ? window->NavLastChildNavWindow->Name : "NULL"); if (window->RootWindow != window) { DebugNodeWindow(window->RootWindow, "RootWindow"); } @@ -13141,7 +14101,7 @@ void ImGui::DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int wi } //----------------------------------------------------------------------------- -// [SECTION] DEBUG LOG +// [SECTION] DEBUG LOG WINDOW //----------------------------------------------------------------------------- void ImGui::DebugLog(const char* fmt, ...) @@ -13160,6 +14120,7 @@ void ImGui::DebugLogV(const char* fmt, va_list args) g.DebugLogBuf.appendfv(fmt, args); if (g.DebugLogFlags & ImGuiDebugLogFlags_OutputToTTY) IMGUI_DEBUG_PRINTF("%s", g.DebugLogBuf.begin() + old_size); + g.DebugLogIndex.append(g.DebugLogBuf.c_str(), old_size, g.DebugLogBuf.size()); } void ImGui::ShowDebugLogWindow(bool* p_open) @@ -13180,14 +14141,42 @@ void ImGui::ShowDebugLogWindow(bool* p_open) SameLine(); CheckboxFlags("Focus", &g.DebugLogFlags, ImGuiDebugLogFlags_EventFocus); SameLine(); CheckboxFlags("Popup", &g.DebugLogFlags, ImGuiDebugLogFlags_EventPopup); SameLine(); CheckboxFlags("Nav", &g.DebugLogFlags, ImGuiDebugLogFlags_EventNav); + SameLine(); CheckboxFlags("Clipper", &g.DebugLogFlags, ImGuiDebugLogFlags_EventClipper); + SameLine(); CheckboxFlags("IO", &g.DebugLogFlags, ImGuiDebugLogFlags_EventIO); if (SmallButton("Clear")) + { g.DebugLogBuf.clear(); + g.DebugLogIndex.clear(); + } SameLine(); if (SmallButton("Copy")) SetClipboardText(g.DebugLogBuf.c_str()); BeginChild("##log", ImVec2(0.0f, 0.0f), true, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar); - TextUnformatted(g.DebugLogBuf.begin(), g.DebugLogBuf.end()); // FIXME-OPT: Could use a line index, but TextUnformatted() has a semi-decent fast path for large text. + + ImGuiListClipper clipper; + clipper.Begin(g.DebugLogIndex.size()); + while (clipper.Step()) + for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++) + { + const char* line_begin = g.DebugLogIndex.get_line_begin(g.DebugLogBuf.c_str(), line_no); + const char* line_end = g.DebugLogIndex.get_line_end(g.DebugLogBuf.c_str(), line_no); + TextUnformatted(line_begin, line_end); + ImRect text_rect = g.LastItemData.Rect; + if (IsItemHovered()) + for (const char* p = line_begin; p < line_end - 10; p++) + { + ImGuiID id = 0; + if (p[0] != '0' || (p[1] != 'x' && p[1] != 'X') || sscanf(p + 2, "%X", &id) != 1) + continue; + ImVec2 p0 = CalcTextSize(line_begin, p); + ImVec2 p1 = CalcTextSize(p, p + 10); + g.LastItemData.Rect = ImRect(text_rect.Min + ImVec2(p0.x, 0.0f), text_rect.Min + ImVec2(p0.x + p1.x, p1.y)); + if (IsMouseHoveringRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, true)) + DebugLocateItemOnHover(id); + p += 10; + } + } if (GetScrollY() >= GetScrollMaxY()) SetScrollHereY(1.0f); EndChild(); @@ -13199,6 +14188,38 @@ void ImGui::ShowDebugLogWindow(bool* p_open) // [SECTION] OTHER DEBUG TOOLS (ITEM PICKER, STACK TOOL) //----------------------------------------------------------------------------- +static const ImU32 DEBUG_LOCATE_ITEM_COLOR = IM_COL32(0, 255, 0, 255); // Green + +void ImGui::DebugLocateItem(ImGuiID target_id) +{ + ImGuiContext& g = *GImGui; + g.DebugLocateId = target_id; + g.DebugLocateFrames = 2; +} + +void ImGui::DebugLocateItemOnHover(ImGuiID target_id) +{ + if (target_id == 0 || !IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenBlockedByPopup)) + return; + ImGuiContext& g = *GImGui; + DebugLocateItem(target_id); + GetForegroundDrawList(g.CurrentWindow)->AddRect(g.LastItemData.Rect.Min - ImVec2(3.0f, 3.0f), g.LastItemData.Rect.Max + ImVec2(3.0f, 3.0f), DEBUG_LOCATE_ITEM_COLOR); +} + +void ImGui::DebugLocateItemResolveWithLastItem() +{ + ImGuiContext& g = *GImGui; + ImGuiLastItemData item_data = g.LastItemData; + g.DebugLocateId = 0; + ImDrawList* draw_list = GetForegroundDrawList(g.CurrentWindow); + ImRect r = item_data.Rect; + r.Expand(3.0f); + ImVec2 p1 = g.IO.MousePos; + ImVec2 p2 = ImVec2((p1.x < r.Min.x) ? r.Min.x : (p1.x > r.Max.x) ? r.Max.x : p1.x, (p1.y < r.Min.y) ? r.Min.y : (p1.y > r.Max.y) ? r.Max.y : p1.y); + draw_list->AddRect(r.Min, r.Max, DEBUG_LOCATE_ITEM_COLOR); + draw_list->AddLine(p1, p2, DEBUG_LOCATE_ITEM_COLOR); +} + // [DEBUG] Item picker tool - start with DebugStartItemPicker() - useful to visually select an item and break into its call-stack. void ImGui::UpdateDebugToolItemPicker() { @@ -13211,16 +14232,24 @@ void ImGui::UpdateDebugToolItemPicker() SetMouseCursor(ImGuiMouseCursor_Hand); if (IsKeyPressed(ImGuiKey_Escape)) g.DebugItemPickerActive = false; - if (IsMouseClicked(0) && hovered_id) + const bool change_mapping = g.IO.KeyMods == (ImGuiMod_Ctrl | ImGuiMod_Shift); + if (!change_mapping && IsMouseClicked(g.DebugItemPickerMouseButton) && hovered_id) { g.DebugItemPickerBreakId = hovered_id; g.DebugItemPickerActive = false; } - SetNextWindowBgAlpha(0.60f); + for (int mouse_button = 0; mouse_button < 3; mouse_button++) + if (change_mapping && IsMouseClicked(mouse_button)) + g.DebugItemPickerMouseButton = (ImU8)mouse_button; + SetNextWindowBgAlpha(0.70f); BeginTooltip(); Text("HoveredId: 0x%08X", hovered_id); Text("Press ESC to abort picking."); - TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click to break in debugger!"); + const char* mouse_button_names[] = { "Left", "Right", "Middle" }; + if (change_mapping) + Text("Remap w/ Ctrl+Shift: click anywhere to select new mouse button."); + else + TextColored(GetStyleColorVec4(hovered_id ? ImGuiCol_Text : ImGuiCol_TextDisabled), "Click %s Button to break in debugger! (remap w/ Ctrl+Shift)", mouse_button_names[g.DebugItemPickerMouseButton]); EndTooltip(); } @@ -13272,7 +14301,7 @@ void ImGui::DebugHookIdInfo(ImGuiID id, ImGuiDataType data_type, const void* dat ImGuiStackTool* tool = &g.DebugStackTool; // Step 0: stack query - // This assume that the ID was computed with the current ID stack, which tends to be the case for our widget. + // This assumes that the ID was computed with the current ID stack, which tends to be the case for our widget. if (tool->StackLevel == -1) { tool->StackLevel++; @@ -13358,7 +14387,7 @@ void ImGui::ShowStackToolWindow(bool* p_open) Checkbox("Ctrl+C: copy path to clipboard", &tool->CopyToClipboardOnCtrlC); SameLine(); TextColored((time_since_copy >= 0.0f && time_since_copy < 0.75f && ImFmod(time_since_copy, 0.25f) < 0.25f * 0.5f) ? ImVec4(1.f, 1.f, 0.3f, 1.f) : ImVec4(), "*COPIED*"); - if (tool->CopyToClipboardOnCtrlC && IsKeyDown(ImGuiKey_ModCtrl) && IsKeyPressed(ImGuiKey_C)) + if (tool->CopyToClipboardOnCtrlC && IsKeyDown(ImGuiMod_Ctrl) && IsKeyPressed(ImGuiKey_C)) { tool->CopyToClipboardLastTime = (float)g.Time; char* p = g.TempBuffer.Data; diff --git a/extern/src/imgui/imgui.h b/extern/src/imgui/imgui.h index bc5fbd61..152f4f09 100644 --- a/extern/src/imgui/imgui.h +++ b/extern/src/imgui/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.88 +// dear imgui, v1.89.2 // (headers) // Help: @@ -20,6 +20,12 @@ // - For first-time users having issues compiling/linking/running or issues loading fonts: // please post in https://github.com/ocornut/imgui/discussions if you cannot find a solution in resources above. +// Library Version +// (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM > 12345') +#define IMGUI_VERSION "1.89.2" +#define IMGUI_VERSION_NUM 18920 +#define IMGUI_HAS_TABLE + /* Index of this file: @@ -42,13 +48,12 @@ Index of this file: #pragma once -// Configuration file with compile-time options (edit imconfig.h or '#define IMGUI_USER_CONFIG "myfilename.h" from your build system') +// Configuration file with compile-time options +// (edit imconfig.h or '#define IMGUI_USER_CONFIG "myfilename.h" from your build system') #ifdef IMGUI_USER_CONFIG #include IMGUI_USER_CONFIG #endif -#if !defined(IMGUI_DISABLE_INCLUDE_IMCONFIG_H) || defined(IMGUI_INCLUDE_IMCONFIG_H) #include "imconfig.h" -#endif #ifndef IMGUI_DISABLE @@ -62,13 +67,6 @@ Index of this file: #include // ptrdiff_t, NULL #include // memset, memmove, memcpy, strlen, strchr, strcpy, strcmp -// Version -// (Integer encoded as XYYZZ for use in #if preprocessor conditionals. Work in progress versions typically starts at XYY99 then bounce up to XYY00, XYY01 etc. when release tagging happens) -#define IMGUI_VERSION "1.88" -#define IMGUI_VERSION_NUM 18800 -#define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) -#define IMGUI_HAS_TABLE - // Define attributes of all API symbols declarations (e.g. for DLL under Windows) // IMGUI_API is used for core imgui functions, IMGUI_IMPL_API is used for the default backends files (imgui_impl_xxx.h) // Using dear imgui via a shared library is not recommended, because we don't guarantee backward nor forward ABI compatibility (also function call overhead, as dear imgui is a call-heavy API) @@ -87,6 +85,7 @@ Index of this file: #define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR) / sizeof(*(_ARR)))) // Size of a static C-style array. Don't use on pointers! #define IM_UNUSED(_VAR) ((void)(_VAR)) // Used to silence "unused variable warnings". Often useful as asserts may be stripped out from final builds. #define IM_OFFSETOF(_TYPE,_MEMBER) offsetof(_TYPE, _MEMBER) // Offset of _MEMBER within _TYPE. Standardized as offsetof() in C++11 +#define IMGUI_CHECKVERSION() ImGui::DebugCheckVersionAndDataLayout(IMGUI_VERSION, sizeof(ImGuiIO), sizeof(ImGuiStyle), sizeof(ImVec2), sizeof(ImVec4), sizeof(ImDrawVert), sizeof(ImDrawIdx)) // Helper Macros - IM_FMTARGS, IM_FMTLIST: Apply printf-style warnings to our formatting functions. #if !defined(IMGUI_USE_STB_SPRINTF) && defined(__MINGW32__) && !defined(__clang__) @@ -162,21 +161,26 @@ struct ImGuiTextBuffer; // Helper to hold and append into a text buf struct ImGuiTextFilter; // Helper to parse and apply text filters (e.g. "aaaaa[,bbbbb][,ccccc]") struct ImGuiViewport; // A Platform Window (always only one in 'master' branch), in the future may represent Platform Monitor -// Enums/Flags (declared as int for compatibility with old C++, to allow using as flags without overhead, and to not pollute the top of this file) +// Enumerations +// - We don't use strongly typed enums much because they add constraints (can't extend in private code, can't store typed in bit fields, extra casting on iteration) // - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists! // In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. // With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments. +enum ImGuiKey : int; // -> enum ImGuiKey // Enum: A key identifier (ImGuiKey_XXX or ImGuiMod_XXX value) typedef int ImGuiCol; // -> enum ImGuiCol_ // Enum: A color identifier for styling typedef int ImGuiCond; // -> enum ImGuiCond_ // Enum: A condition for many Set*() functions typedef int ImGuiDataType; // -> enum ImGuiDataType_ // Enum: A primary data type typedef int ImGuiDir; // -> enum ImGuiDir_ // Enum: A cardinal direction -typedef int ImGuiKey; // -> enum ImGuiKey_ // Enum: A key identifier -typedef int ImGuiNavInput; // -> enum ImGuiNavInput_ // Enum: An input identifier for navigation typedef int ImGuiMouseButton; // -> enum ImGuiMouseButton_ // Enum: A mouse button identifier (0=left, 1=right, 2=middle) -typedef int ImGuiMouseCursor; // -> enum ImGuiMouseCursor_ // Enum: A mouse cursor identifier +typedef int ImGuiMouseCursor; // -> enum ImGuiMouseCursor_ // Enum: A mouse cursor shape typedef int ImGuiSortDirection; // -> enum ImGuiSortDirection_ // Enum: A sorting direction (ascending or descending) typedef int ImGuiStyleVar; // -> enum ImGuiStyleVar_ // Enum: A variable identifier for styling typedef int ImGuiTableBgTarget; // -> enum ImGuiTableBgTarget_ // Enum: A color target for TableSetBgColor() + +// Flags (declared as int for compatibility with old C++, to allow using as flags without overhead, and to not pollute the top of this file) +// - Tip: Use your programming IDE navigation facilities on the names in the _central column_ below to find the actual flags/enum lists! +// In Visual Studio IDE: CTRL+comma ("Edit.GoToAll") can follow symbols in comments, whereas CTRL+F12 ("Edit.GoToImplementation") cannot. +// With Visual Assist installed: ALT+G ("VAssistX.GoToImplementation") can also follow symbols in comments. typedef int ImDrawFlags; // -> enum ImDrawFlags_ // Flags: for ImDrawList functions typedef int ImDrawListFlags; // -> enum ImDrawListFlags_ // Flags: for ImDrawList instance typedef int ImFontAtlasFlags; // -> enum ImFontAtlasFlags_ // Flags: for ImFontAtlas build @@ -189,7 +193,7 @@ typedef int ImGuiDragDropFlags; // -> enum ImGuiDragDropFlags_ // Flags: f typedef int ImGuiFocusedFlags; // -> enum ImGuiFocusedFlags_ // Flags: for IsWindowFocused() typedef int ImGuiHoveredFlags; // -> enum ImGuiHoveredFlags_ // Flags: for IsItemHovered(), IsWindowHovered() etc. typedef int ImGuiInputTextFlags; // -> enum ImGuiInputTextFlags_ // Flags: for InputText(), InputTextMultiline() -typedef int ImGuiModFlags; // -> enum ImGuiModFlags_ // Flags: for io.KeyMods (Ctrl/Shift/Alt/Super) +typedef int ImGuiKeyChord; // -> ImGuiKey | ImGuiMod_XXX // Flags: for storage only for now: an ImGuiKey optionally OR-ed with one or more ImGuiMod_XXX values. typedef int ImGuiPopupFlags; // -> enum ImGuiPopupFlags_ // Flags: for OpenPopup*(), BeginPopupContext*(), IsPopupOpen() typedef int ImGuiSelectableFlags; // -> enum ImGuiSelectableFlags_ // Flags: for Selectable() typedef int ImGuiSliderFlags; // -> enum ImGuiSliderFlags_ // Flags: for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc. @@ -251,8 +255,8 @@ struct ImVec2 float x, y; constexpr ImVec2() : x(0.0f), y(0.0f) { } constexpr ImVec2(float _x, float _y) : x(_x), y(_y) { } - float operator[] (size_t idx) const { IM_ASSERT(idx <= 1); return (&x)[idx]; } // We very rarely use this [] operator, the assert overhead is fine. - float& operator[] (size_t idx) { IM_ASSERT(idx <= 1); return (&x)[idx]; } // We very rarely use this [] operator, the assert overhead is fine. + float operator[] (size_t idx) const { IM_ASSERT(idx == 0 || idx == 1); return (&x)[idx]; } // We very rarely use this [] operator, the assert overhead is fine. + float& operator[] (size_t idx) { IM_ASSERT(idx == 0 || idx == 1); return (&x)[idx]; } // We very rarely use this [] operator, the assert overhead is fine. #ifdef IM_VEC2_CLASS_EXTRA IM_VEC2_CLASS_EXTRA // Define additional constructors and implicit cast operators in imconfig.h to convert back and forth between your math types and ImVec2. #endif @@ -303,7 +307,7 @@ namespace ImGui IMGUI_API void ShowStyleEditor(ImGuiStyle* ref = NULL); // add style editor block (not a window). you can pass in a reference ImGuiStyle structure to compare to, revert to and save to (else it uses the default style) IMGUI_API bool ShowStyleSelector(const char* label); // add style selector block (not a window), essentially a combo listing the default styles. IMGUI_API void ShowFontSelector(const char* label); // add font selector block (not a window), essentially a combo listing the loaded fonts. - IMGUI_API void ShowUserGuide(); // add basic help/info block (not a window): how to manipulate ImGui as a end-user (mouse/keyboard controls). + IMGUI_API void ShowUserGuide(); // add basic help/info block (not a window): how to manipulate ImGui as an end-user (mouse/keyboard controls). IMGUI_API const char* GetVersion(); // get the compiled version string e.g. "1.80 WIP" (essentially the value for IMGUI_VERSION from the compiled version of imgui.cpp) // Styles @@ -358,6 +362,7 @@ namespace ImGui IMGUI_API void SetNextWindowContentSize(const ImVec2& size); // set next window content size (~ scrollable client area, which enforce the range of scrollbars). Not including window decorations (title bar, menu bar, etc.) nor WindowPadding. set an axis to 0.0f to leave it automatic. call before Begin() IMGUI_API void SetNextWindowCollapsed(bool collapsed, ImGuiCond cond = 0); // set next window collapsed state. call before Begin() IMGUI_API void SetNextWindowFocus(); // set next window to be focused / top-most. call before Begin() + IMGUI_API void SetNextWindowScroll(const ImVec2& scroll); // set next window scrolling value (use < 0.0f to not affect a given axis). IMGUI_API void SetNextWindowBgAlpha(float alpha); // set next window background color alpha. helper to easily override the Alpha component of ImGuiCol_WindowBg/ChildBg/PopupBg. you may also use ImGuiWindowFlags_NoBackground. IMGUI_API void SetWindowPos(const ImVec2& pos, ImGuiCond cond = 0); // (not recommended) set current window position - call within Begin()/End(). prefer using SetNextWindowPos(), as this may incur tearing and side-effects. IMGUI_API void SetWindowSize(const ImVec2& size, ImGuiCond cond = 0); // (not recommended) set current window size - call within Begin()/End(). set to ImVec2(0, 0) to force an auto-fit. prefer using SetNextWindowSize(), as this may incur tearing and minor side-effects. @@ -375,9 +380,11 @@ namespace ImGui IMGUI_API ImVec2 GetContentRegionAvail(); // == GetContentRegionMax() - GetCursorPos() IMGUI_API ImVec2 GetContentRegionMax(); // current content boundaries (typically window boundaries including scrolling, or current column boundaries), in windows coordinates IMGUI_API ImVec2 GetWindowContentRegionMin(); // content boundaries min for the full window (roughly (0,0)-Scroll), in window coordinates - IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max for the full window (roughly (0,0)+Size-Scroll) where Size can be override with SetNextWindowContentSize(), in window coordinates + IMGUI_API ImVec2 GetWindowContentRegionMax(); // content boundaries max for the full window (roughly (0,0)+Size-Scroll) where Size can be overridden with SetNextWindowContentSize(), in window coordinates // Windows Scrolling + // - Any change of Scroll will be applied at the beginning of next frame in the first call to Begin(). + // - You may instead use SetNextWindowScroll() prior to calling Begin() to avoid this delay, as an alternative to using SetScrollX()/SetScrollY(). IMGUI_API float GetScrollX(); // get scrolling amount [0 .. GetScrollMaxX()] IMGUI_API float GetScrollY(); // get scrolling amount [0 .. GetScrollMaxY()] IMGUI_API void SetScrollX(float scroll_x); // set scrolling amount [0 .. GetScrollMaxX()] @@ -412,7 +419,7 @@ namespace ImGui IMGUI_API void PopTextWrapPos(); // Style read access - // - Use the style editor (ShowStyleEditor() function) to interactively see what the colors are) + // - Use the ShowStyleEditor() function to interactively see/edit the colors. IMGUI_API ImFont* GetFont(); // get current font IMGUI_API float GetFontSize(); // get current font size (= height in pixels) of current font with current scale applied IMGUI_API ImVec2 GetFontTexUvWhitePixel(); // get UV coordinate for a while pixel, useful to draw custom shapes via the ImDrawList API @@ -430,7 +437,7 @@ namespace ImGui // Absolute coordinate: GetCursorScreenPos(), SetCursorScreenPos(), all ImDrawList:: functions. IMGUI_API void Separator(); // separator, generally horizontal. inside a menu bar or in horizontal layout mode, this becomes a vertical separator. IMGUI_API void SameLine(float offset_from_start_x=0.0f, float spacing=-1.0f); // call between widgets or groups to layout them horizontally. X position given in window coordinates. - IMGUI_API void NewLine(); // undo a SameLine() or force a new line when in an horizontal-layout context. + IMGUI_API void NewLine(); // undo a SameLine() or force a new line when in a horizontal-layout context. IMGUI_API void Spacing(); // add vertical spacing. IMGUI_API void Dummy(const ImVec2& size); // add a dummy item of given size. unlike InvisibleButton(), Dummy() won't take the mouse click or be navigable into. IMGUI_API void Indent(float indent_w = 0.0f); // move content position toward the right, by indent_w, or style.IndentSpacing if indent_w <= 0 @@ -494,8 +501,6 @@ namespace ImGui IMGUI_API bool SmallButton(const char* label); // button with FramePadding=(0,0) to easily embed within text IMGUI_API bool InvisibleButton(const char* str_id, const ImVec2& size, ImGuiButtonFlags flags = 0); // flexible button behavior without the visuals, frequently useful to build custom behaviors using the public api (along with IsItemActive, IsItemHovered, etc.) IMGUI_API bool ArrowButton(const char* str_id, ImGuiDir dir); // square button with an arrow shape - IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1,1), const ImVec4& tint_col = ImVec4(1,1,1,1), const ImVec4& border_col = ImVec4(0,0,0,0)); - IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1,1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0,0,0,0), const ImVec4& tint_col = ImVec4(1,1,1,1)); // <0 frame_padding uses default frame padding settings. 0 for no padding IMGUI_API bool Checkbox(const char* label, bool* v); IMGUI_API bool CheckboxFlags(const char* label, int* flags, int flags_value); IMGUI_API bool CheckboxFlags(const char* label, unsigned int* flags, unsigned int flags_value); @@ -504,7 +509,12 @@ namespace ImGui IMGUI_API void ProgressBar(float fraction, const ImVec2& size_arg = ImVec2(-FLT_MIN, 0), const char* overlay = NULL); IMGUI_API void Bullet(); // draw a small circle + keep the cursor on the same line. advance cursor x position by GetTreeNodeToLabelSpacing(), same distance that TreeNode() uses - // Widgets: Combo Box + // Widgets: Images + // - Read about ImTextureID here: https://github.com/ocornut/imgui/wiki/Image-Loading-and-Displaying-Examples + IMGUI_API void Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& tint_col = ImVec4(1, 1, 1, 1), const ImVec4& border_col = ImVec4(0, 0, 0, 0)); + IMGUI_API bool ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); + + // Widgets: Combo Box (Dropdown) // - The BeginCombo()/EndCombo() api allows you to manage your contents and selection state however you want it, by creating e.g. Selectable() items. // - The old Combo() api are helpers over BeginCombo()/EndCombo() which are kept available for convenience purpose. This is analogous to how ListBox are created. IMGUI_API bool BeginCombo(const char* label, const char* preview_value, ImGuiComboFlags flags = 0); @@ -515,7 +525,7 @@ namespace ImGui // Widgets: Drag Sliders // - CTRL+Click on any drag box to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp. - // - For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every functions, note that a 'float v[X]' function argument is the same as 'float* v', + // - For all the Float2/Float3/Float4/Int2/Int3/Int4 versions of every function, note that a 'float v[X]' function argument is the same as 'float* v', // the array syntax is just a way to document the number of elements that are expected to be accessible. You can pass address of your first element out of a contiguous set, e.g. &myvector.x // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. // - Format string may also be set to NULL or use the default format ("%f" or "%d"). @@ -523,7 +533,7 @@ namespace ImGui // - Use v_min < v_max to clamp edits to given limits. Note that CTRL+Click manual input can override those limits if ImGuiSliderFlags_AlwaysClamp is not used. // - Use v_max = FLT_MAX / INT_MAX etc to avoid clamping to a maximum, same with v_min = -FLT_MAX / INT_MIN to avoid clamping to a minimum. // - We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them. - // - Legacy: Pre-1.78 there are DragXXX() function signatures that takes a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument. + // - Legacy: Pre-1.78 there are DragXXX() function signatures that take a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument. // If you get a warning converting a float to ImGuiSliderFlags, read https://github.com/ocornut/imgui/issues/3361 IMGUI_API bool DragFloat(const char* label, float* v, float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", ImGuiSliderFlags flags = 0); // If v_min >= v_max we have no bound IMGUI_API bool DragFloat2(const char* label, float v[2], float v_speed = 1.0f, float v_min = 0.0f, float v_max = 0.0f, const char* format = "%.3f", ImGuiSliderFlags flags = 0); @@ -542,7 +552,7 @@ namespace ImGui // - CTRL+Click on any slider to turn them into an input box. Manually input values aren't clamped by default and can go off-bounds. Use ImGuiSliderFlags_AlwaysClamp to always clamp. // - Adjust format string to decorate the value with a prefix, a suffix, or adapt the editing and display precision e.g. "%.3f" -> 1.234; "%5.2f secs" -> 01.23 secs; "Biscuit: %.0f" -> Biscuit: 1; etc. // - Format string may also be set to NULL or use the default format ("%f" or "%d"). - // - Legacy: Pre-1.78 there are SliderXXX() function signatures that takes a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument. + // - Legacy: Pre-1.78 there are SliderXXX() function signatures that take a final `float power=1.0f' argument instead of the `ImGuiSliderFlags flags=0' argument. // If you get a warning converting a float to ImGuiSliderFlags, read https://github.com/ocornut/imgui/issues/3361 IMGUI_API bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0); // adjust format to decorate the value with a prefix or a suffix for in-slider labels or unit display. IMGUI_API bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format = "%.3f", ImGuiSliderFlags flags = 0); @@ -600,7 +610,7 @@ namespace ImGui IMGUI_API bool TreeNodeExV(const char* str_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) IM_FMTLIST(3); IMGUI_API bool TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char* fmt, va_list args) IM_FMTLIST(3); IMGUI_API void TreePush(const char* str_id); // ~ Indent()+PushId(). Already called by TreeNode() when returning true, but you can call TreePush/TreePop yourself if desired. - IMGUI_API void TreePush(const void* ptr_id = NULL); // " + IMGUI_API void TreePush(const void* ptr_id); // " IMGUI_API void TreePop(); // ~ Unindent()+PopId() IMGUI_API float GetTreeNodeToLabelSpacing(); // horizontal distance preceding label when using TreeNode*() or Bullet() == (g.FontSize + style.FramePadding.x*2) for a regular unframed TreeNode IMGUI_API bool CollapsingHeader(const char* label, ImGuiTreeNodeFlags flags = 0); // if returning 'true' the header is open. doesn't indent nor push on ID stack. user doesn't have to call TreePop(). @@ -670,7 +680,7 @@ namespace ImGui // Popups: begin/end functions // - BeginPopup(): query popup state, if open start appending into the window. Call EndPopup() afterwards. ImGuiWindowFlags are forwarded to the window. - // - BeginPopupModal(): block every interactions behind the window, cannot be closed by user, add a dimming background, has a title bar. + // - BeginPopupModal(): block every interaction behind the window, cannot be closed by user, add a dimming background, has a title bar. IMGUI_API bool BeginPopup(const char* str_id, ImGuiWindowFlags flags = 0); // return true if the popup is open, and you can start outputting to it. IMGUI_API bool BeginPopupModal(const char* name, bool* p_open = NULL, ImGuiWindowFlags flags = 0); // return true if the modal is open, and you can start outputting to it. IMGUI_API void EndPopup(); // only call EndPopup() if BeginPopupXXX() returns true! @@ -714,7 +724,7 @@ namespace ImGui // - 4. Optionally call TableHeadersRow() to submit a header row. Names are pulled from TableSetupColumn() data. // - 5. Populate contents: // - In most situations you can use TableNextRow() + TableSetColumnIndex(N) to start appending into a column. - // - If you are using tables as a sort of grid, where every columns is holding the same type of contents, + // - If you are using tables as a sort of grid, where every column is holding the same type of contents, // you may prefer using TableNextColumn() instead of TableNextRow() + TableSetColumnIndex(). // TableNextColumn() will automatically wrap-around into the next row if needed. // - IMPORTANT: Comparatively to the old Columns() API, we need to call TableNextColumn() for the first column! @@ -772,6 +782,7 @@ namespace ImGui IMGUI_API int GetColumnsCount(); // Tab Bars, Tabs + // - Note: Tabs are automatically created by the docking system (when in 'docking' branch). Use this to create tab bars/tabs yourself. IMGUI_API bool BeginTabBar(const char* str_id, ImGuiTabBarFlags flags = 0); // create and append into a TabBar IMGUI_API void EndTabBar(); // only call EndTabBar() if BeginTabBar() returns true! IMGUI_API bool BeginTabItem(const char* label, bool* p_open = NULL, ImGuiTabItemFlags flags = 0); // create a Tab. Returns true if the Tab is selected. @@ -825,16 +836,17 @@ namespace ImGui IMGUI_API bool IsItemHovered(ImGuiHoveredFlags flags = 0); // is the last item hovered? (and usable, aka not blocked by a popup, etc.). See ImGuiHoveredFlags for more options. IMGUI_API bool IsItemActive(); // is the last item active? (e.g. button being held, text field being edited. This will continuously return true while holding mouse button on an item. Items that don't interact will always return false) IMGUI_API bool IsItemFocused(); // is the last item focused for keyboard/gamepad navigation? - IMGUI_API bool IsItemClicked(ImGuiMouseButton mouse_button = 0); // is the last item hovered and mouse clicked on? (**) == IsMouseClicked(mouse_button) && IsItemHovered()Important. (**) this it NOT equivalent to the behavior of e.g. Button(). Read comments in function definition. + IMGUI_API bool IsItemClicked(ImGuiMouseButton mouse_button = 0); // is the last item hovered and mouse clicked on? (**) == IsMouseClicked(mouse_button) && IsItemHovered()Important. (**) this is NOT equivalent to the behavior of e.g. Button(). Read comments in function definition. IMGUI_API bool IsItemVisible(); // is the last item visible? (items may be out of sight because of clipping/scrolling) IMGUI_API bool IsItemEdited(); // did the last item modify its underlying value this frame? or was pressed? This is generally the same as the "bool" return value of many widgets. IMGUI_API bool IsItemActivated(); // was the last item just made active (item was previously inactive). - IMGUI_API bool IsItemDeactivated(); // was the last item just made inactive (item was previously active). Useful for Undo/Redo patterns with widgets that requires continuous editing. - IMGUI_API bool IsItemDeactivatedAfterEdit(); // was the last item just made inactive and made a value change when it was active? (e.g. Slider/Drag moved). Useful for Undo/Redo patterns with widgets that requires continuous editing. Note that you may get false positives (some widgets such as Combo()/ListBox()/Selectable() will return true even when clicking an already selected item). + IMGUI_API bool IsItemDeactivated(); // was the last item just made inactive (item was previously active). Useful for Undo/Redo patterns with widgets that require continuous editing. + IMGUI_API bool IsItemDeactivatedAfterEdit(); // was the last item just made inactive and made a value change when it was active? (e.g. Slider/Drag moved). Useful for Undo/Redo patterns with widgets that require continuous editing. Note that you may get false positives (some widgets such as Combo()/ListBox()/Selectable() will return true even when clicking an already selected item). IMGUI_API bool IsItemToggledOpen(); // was the last item open state toggled? set by TreeNode(). IMGUI_API bool IsAnyItemHovered(); // is any item hovered? IMGUI_API bool IsAnyItemActive(); // is any item active? IMGUI_API bool IsAnyItemFocused(); // is any item focused? + IMGUI_API ImGuiID GetItemID(); // get ID of last item (~~ often same ImGui::GetID(label) beforehand) IMGUI_API ImVec2 GetItemRectMin(); // get upper-left bounding rectangle of the last item (screen space) IMGUI_API ImVec2 GetItemRectMax(); // get lower-right bounding rectangle of the last item (screen space) IMGUI_API ImVec2 GetItemRectSize(); // get size of last item @@ -871,12 +883,11 @@ namespace ImGui IMGUI_API void ColorConvertRGBtoHSV(float r, float g, float b, float& out_h, float& out_s, float& out_v); IMGUI_API void ColorConvertHSVtoRGB(float h, float s, float v, float& out_r, float& out_g, float& out_b); - // Inputs Utilities: Keyboard - // Without IMGUI_DISABLE_OBSOLETE_KEYIO: (legacy support) - // - For 'ImGuiKey key' you can still use your legacy native/user indices according to how your backend/engine stored them in io.KeysDown[]. - // With IMGUI_DISABLE_OBSOLETE_KEYIO: (this is the way forward) - // - Any use of 'ImGuiKey' will assert when key < 512 will be passed, previously reserved as native/user keys indices - // - GetKeyIndex() is pass-through and therefore deprecated (gone if IMGUI_DISABLE_OBSOLETE_KEYIO is defined) + // Inputs Utilities: Keyboard/Mouse/Gamepad + // - the ImGuiKey enum contains all possible keyboard, mouse and gamepad inputs (e.g. ImGuiKey_A, ImGuiKey_MouseLeft, ImGuiKey_GamepadDpadUp...). + // - before v1.87, we used ImGuiKey to carry native/user indices as defined by each backends. About use of those legacy ImGuiKey values: + // - without IMGUI_DISABLE_OBSOLETE_KEYIO (legacy support): you can still use your legacy native/user indices (< 512) according to how your backend/engine stored them in io.KeysDown[], but need to cast them to ImGuiKey. + // - with IMGUI_DISABLE_OBSOLETE_KEYIO (this is the way forward): any use of ImGuiKey will assert with key < 512. GetKeyIndex() is pass-through and therefore deprecated (gone if IMGUI_DISABLE_OBSOLETE_KEYIO is defined). IMGUI_API bool IsKeyDown(ImGuiKey key); // is key being held. IMGUI_API bool IsKeyPressed(ImGuiKey key, bool repeat = true); // was key pressed (went from !Down to Down)? if repeat=true, uses io.KeyRepeatDelay / KeyRepeatRate IMGUI_API bool IsKeyReleased(ImGuiKey key); // was key released (went from Down to !Down)? @@ -884,7 +895,7 @@ namespace ImGui IMGUI_API const char* GetKeyName(ImGuiKey key); // [DEBUG] returns English name of the key. Those names a provided for debugging purpose and are not meant to be saved persistently not compared. IMGUI_API void SetNextFrameWantCaptureKeyboard(bool want_capture_keyboard); // Override io.WantCaptureKeyboard flag next frame (said flag is left for your application to handle, typically when true it instructs your app to ignore inputs). e.g. force capture keyboard when your widget is being hovered. This is equivalent to setting "io.WantCaptureKeyboard = want_capture_keyboard"; after the next NewFrame() call. - // Inputs Utilities: Mouse + // Inputs Utilities: Mouse specific // - To refer to a mouse button, you may use named enums in your code e.g. ImGuiMouseButton_Left, ImGuiMouseButton_Right. // - You can also use regular integer: it is forever guaranteed that 0=Left, 1=Right, 2=Middle. // - Dragging operations are only reported after mouse has moved a certain distance away from the initial clicking position (see 'lock_threshold' and 'io.MouseDraggingThreshold') @@ -901,8 +912,8 @@ namespace ImGui IMGUI_API bool IsMouseDragging(ImGuiMouseButton button, float lock_threshold = -1.0f); // is mouse dragging? (if lock_threshold < -1.0f, uses io.MouseDraggingThreshold) IMGUI_API ImVec2 GetMouseDragDelta(ImGuiMouseButton button = 0, float lock_threshold = -1.0f); // return the delta from the initial clicking position while the mouse button is pressed or was just released. This is locked and return 0.0f until the mouse moves past a distance threshold at least once (if lock_threshold < -1.0f, uses io.MouseDraggingThreshold) IMGUI_API void ResetMouseDragDelta(ImGuiMouseButton button = 0); // - IMGUI_API ImGuiMouseCursor GetMouseCursor(); // get desired cursor type, reset in ImGui::NewFrame(), this is updated during the frame. valid before Render(). If you use software rendering by setting io.MouseDrawCursor ImGui will render those for you - IMGUI_API void SetMouseCursor(ImGuiMouseCursor cursor_type); // set desired cursor type + IMGUI_API ImGuiMouseCursor GetMouseCursor(); // get desired mouse cursor shape. Important: reset in ImGui::NewFrame(), this is updated during the frame. valid before Render(). If you use software rendering by setting io.MouseDrawCursor ImGui will render those for you + IMGUI_API void SetMouseCursor(ImGuiMouseCursor cursor_type); // set desired mouse cursor shape IMGUI_API void SetNextFrameWantCaptureMouse(bool want_capture_mouse); // Override io.WantCaptureMouse flag next frame (said flag is left for your application to handle, typical when true it instucts your app to ignore inputs). This is equivalent to setting "io.WantCaptureMouse = want_capture_mouse;" after the next NewFrame() call. // Clipboard Utilities @@ -939,6 +950,7 @@ namespace ImGui //----------------------------------------------------------------------------- // Flags for ImGui::Begin() +// (Those are per-window flags. There are shared flags in ImGuiIO: io.ConfigWindowsResizeFromEdges and io.ConfigWindowsMoveFromTitleBarOnly) enum ImGuiWindowFlags_ { ImGuiWindowFlags_None = 0, @@ -972,11 +984,11 @@ enum ImGuiWindowFlags_ ImGuiWindowFlags_Tooltip = 1 << 25, // Don't use! For internal use by BeginTooltip() ImGuiWindowFlags_Popup = 1 << 26, // Don't use! For internal use by BeginPopup() ImGuiWindowFlags_Modal = 1 << 27, // Don't use! For internal use by BeginPopupModal() - ImGuiWindowFlags_ChildMenu = 1 << 28 // Don't use! For internal use by BeginMenu() - //ImGuiWindowFlags_ResizeFromAnySide = 1 << 17, // [Obsolete] --> Set io.ConfigWindowsResizeFromEdges=true and make sure mouse cursors are supported by backend (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) + ImGuiWindowFlags_ChildMenu = 1 << 28, // Don't use! For internal use by BeginMenu() }; // Flags for ImGui::InputText() +// (Those are per-item flags. There are shared flags in ImGuiIO: io.ConfigInputTextCursorBlink and io.ConfigInputTextEnterKeepActive) enum ImGuiInputTextFlags_ { ImGuiInputTextFlags_None = 0, @@ -999,11 +1011,12 @@ enum ImGuiInputTextFlags_ ImGuiInputTextFlags_NoUndoRedo = 1 << 16, // Disable undo/redo. Note that input text owns the text data while active, if you want to provide your own undo/redo stack you need e.g. to call ClearActiveID(). ImGuiInputTextFlags_CharsScientific = 1 << 17, // Allow 0123456789.+-*/eE (Scientific notation input) ImGuiInputTextFlags_CallbackResize = 1 << 18, // Callback on buffer capacity changes request (beyond 'buf_size' parameter value), allowing the string to grow. Notify when the string wants to be resized (for string types which hold a cache of their Size). You will be provided a new BufSize in the callback and NEED to honor it. (see misc/cpp/imgui_stdlib.h for an example of using this) - ImGuiInputTextFlags_CallbackEdit = 1 << 19 // Callback on any edit (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active) + ImGuiInputTextFlags_CallbackEdit = 1 << 19, // Callback on any edit (note that InputText() already returns true on edit, the callback is useful mainly to manipulate the underlying buffer while focus is active) + ImGuiInputTextFlags_EscapeClearsAll = 1 << 20, // Escape key clears content if not empty, and deactivate otherwise (contrast to default behavior of Escape to revert) // Obsolete names (will be removed soon) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - , ImGuiInputTextFlags_AlwaysInsertMode = ImGuiInputTextFlags_AlwaysOverwrite // [renamed in 1.82] name was not matching behavior + ImGuiInputTextFlags_AlwaysInsertMode = ImGuiInputTextFlags_AlwaysOverwrite // [renamed in 1.82] name was not matching behavior #endif }; @@ -1026,7 +1039,7 @@ enum ImGuiTreeNodeFlags_ ImGuiTreeNodeFlags_SpanFullWidth = 1 << 12, // Extend hit box to the left-most and right-most edges (bypass the indented area). ImGuiTreeNodeFlags_NavLeftJumpsBackHere = 1 << 13, // (WIP) Nav: left direction may move to this TreeNode() from any of its child (items submitted between TreeNode and TreePop) //ImGuiTreeNodeFlags_NoScrollOnOpen = 1 << 14, // FIXME: TODO: Disable automatic scroll on TreePop() if node got just open and contents is not visible - ImGuiTreeNodeFlags_CollapsingHeader = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_NoAutoOpenOnLog + ImGuiTreeNodeFlags_CollapsingHeader = ImGuiTreeNodeFlags_Framed | ImGuiTreeNodeFlags_NoTreePushOnOpen | ImGuiTreeNodeFlags_NoAutoOpenOnLog, }; // Flags for OpenPopup*(), BeginPopupContext*(), IsPopupOpen() functions. @@ -1035,7 +1048,7 @@ enum ImGuiTreeNodeFlags_ // It is therefore guaranteed to be legal to pass a mouse button index in ImGuiPopupFlags. // - For the same reason, we exceptionally default the ImGuiPopupFlags argument of BeginPopupContextXXX functions to 1 instead of 0. // IMPORTANT: because the default parameter is 1 (==ImGuiPopupFlags_MouseButtonRight), if you rely on the default parameter -// and want to another another flag, you need to pass in the ImGuiPopupFlags_MouseButtonRight flag. +// and want to use another flag, you need to pass in the ImGuiPopupFlags_MouseButtonRight flag explicitly. // - Multiple buttons currently cannot be combined/or-ed in those functions (we could allow it later). enum ImGuiPopupFlags_ { @@ -1049,18 +1062,18 @@ enum ImGuiPopupFlags_ ImGuiPopupFlags_NoOpenOverItems = 1 << 6, // For BeginPopupContextWindow(): don't return true when hovering items, only when hovering empty space ImGuiPopupFlags_AnyPopupId = 1 << 7, // For IsPopupOpen(): ignore the ImGuiID parameter and test for any popup. ImGuiPopupFlags_AnyPopupLevel = 1 << 8, // For IsPopupOpen(): search/test at any level of the popup stack (default test in the current level) - ImGuiPopupFlags_AnyPopup = ImGuiPopupFlags_AnyPopupId | ImGuiPopupFlags_AnyPopupLevel + ImGuiPopupFlags_AnyPopup = ImGuiPopupFlags_AnyPopupId | ImGuiPopupFlags_AnyPopupLevel, }; // Flags for ImGui::Selectable() enum ImGuiSelectableFlags_ { ImGuiSelectableFlags_None = 0, - ImGuiSelectableFlags_DontClosePopups = 1 << 0, // Clicking this don't close parent popup window + ImGuiSelectableFlags_DontClosePopups = 1 << 0, // Clicking this doesn't close parent popup window ImGuiSelectableFlags_SpanAllColumns = 1 << 1, // Selectable frame can span all columns (text will still fit in current column) ImGuiSelectableFlags_AllowDoubleClick = 1 << 2, // Generate press events on double clicks too ImGuiSelectableFlags_Disabled = 1 << 3, // Cannot be selected, display grayed out text - ImGuiSelectableFlags_AllowItemOverlap = 1 << 4 // (WIP) Hit testing to allow subsequent widgets to overlap this one + ImGuiSelectableFlags_AllowItemOverlap = 1 << 4, // (WIP) Hit testing to allow subsequent widgets to overlap this one }; // Flags for ImGui::BeginCombo() @@ -1074,7 +1087,7 @@ enum ImGuiComboFlags_ ImGuiComboFlags_HeightLargest = 1 << 4, // As many fitting items as possible ImGuiComboFlags_NoArrowButton = 1 << 5, // Display on the preview box without the square arrow button ImGuiComboFlags_NoPreview = 1 << 6, // Display only a square arrow button - ImGuiComboFlags_HeightMask_ = ImGuiComboFlags_HeightSmall | ImGuiComboFlags_HeightRegular | ImGuiComboFlags_HeightLarge | ImGuiComboFlags_HeightLargest + ImGuiComboFlags_HeightMask_ = ImGuiComboFlags_HeightSmall | ImGuiComboFlags_HeightRegular | ImGuiComboFlags_HeightLarge | ImGuiComboFlags_HeightLargest, }; // Flags for ImGui::BeginTabBar() @@ -1090,7 +1103,7 @@ enum ImGuiTabBarFlags_ ImGuiTabBarFlags_FittingPolicyResizeDown = 1 << 6, // Resize tabs when they don't fit ImGuiTabBarFlags_FittingPolicyScroll = 1 << 7, // Add scroll buttons when tabs don't fit ImGuiTabBarFlags_FittingPolicyMask_ = ImGuiTabBarFlags_FittingPolicyResizeDown | ImGuiTabBarFlags_FittingPolicyScroll, - ImGuiTabBarFlags_FittingPolicyDefault_ = ImGuiTabBarFlags_FittingPolicyResizeDown + ImGuiTabBarFlags_FittingPolicyDefault_ = ImGuiTabBarFlags_FittingPolicyResizeDown, }; // Flags for ImGui::BeginTabItem() @@ -1104,7 +1117,7 @@ enum ImGuiTabItemFlags_ ImGuiTabItemFlags_NoTooltip = 1 << 4, // Disable tooltip for the given tab ImGuiTabItemFlags_NoReorder = 1 << 5, // Disable reordering this tab or having another tab cross over this tab ImGuiTabItemFlags_Leading = 1 << 6, // Enforce the tab position to the left of the tab bar (after the tab list popup button) - ImGuiTabItemFlags_Trailing = 1 << 7 // Enforce the tab position to the right of the tab bar (before the scrolling buttons) + ImGuiTabItemFlags_Trailing = 1 << 7, // Enforce the tab position to the right of the tab bar (before the scrolling buttons) }; // Flags for ImGui::BeginTable() @@ -1124,7 +1137,7 @@ enum ImGuiTabItemFlags_ // - When ScrollX is on: // - Table defaults to ImGuiTableFlags_SizingFixedFit -> all Columns defaults to ImGuiTableColumnFlags_WidthFixed // - Columns sizing policy allowed: Fixed/Auto mostly. -// - Fixed Columns can be enlarged as needed. Table will show an horizontal scrollbar if needed. +// - Fixed Columns can be enlarged as needed. Table will show a horizontal scrollbar if needed. // - When using auto-resizing (non-resizable) fixed columns, querying the content width to use item right-alignment e.g. SetNextItemWidth(-FLT_MIN) doesn't make sense, would create a feedback loop. // - Using Stretch columns OFTEN DOES NOT MAKE SENSE if ScrollX is on, UNLESS you have specified a value for 'inner_width' in BeginTable(). // If you specify a value for 'inner_width' then effectively the scrolling space is known and Stretch or mixed Fixed/Stretch columns become meaningful again. @@ -1150,8 +1163,8 @@ enum ImGuiTableFlags_ ImGuiTableFlags_BordersInner = ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_BordersInnerH, // Draw inner borders. ImGuiTableFlags_BordersOuter = ImGuiTableFlags_BordersOuterV | ImGuiTableFlags_BordersOuterH, // Draw outer borders. ImGuiTableFlags_Borders = ImGuiTableFlags_BordersInner | ImGuiTableFlags_BordersOuter, // Draw all borders. - ImGuiTableFlags_NoBordersInBody = 1 << 11, // [ALPHA] Disable vertical borders in columns Body (borders will always appears in Headers). -> May move to style - ImGuiTableFlags_NoBordersInBodyUntilResize = 1 << 12, // [ALPHA] Disable vertical borders in columns Body until hovered for resize (borders will always appears in Headers). -> May move to style + ImGuiTableFlags_NoBordersInBody = 1 << 11, // [ALPHA] Disable vertical borders in columns Body (borders will always appear in Headers). -> May move to style + ImGuiTableFlags_NoBordersInBodyUntilResize = 1 << 12, // [ALPHA] Disable vertical borders in columns Body until hovered for resize (borders will always appear in Headers). -> May move to style // Sizing Policy (read above for defaults) ImGuiTableFlags_SizingFixedFit = 1 << 13, // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching contents width. ImGuiTableFlags_SizingFixedSame = 2 << 13, // Columns default to _WidthFixed or _WidthAuto (if resizable or not resizable), matching the maximum contents width of all columns. Implicitly enable ImGuiTableFlags_NoKeepColumnsVisible. @@ -1165,24 +1178,18 @@ enum ImGuiTableFlags_ // Clipping ImGuiTableFlags_NoClip = 1 << 20, // Disable clipping rectangle for every individual columns (reduce draw command count, items will be able to overflow into other columns). Generally incompatible with TableSetupScrollFreeze(). // Padding - ImGuiTableFlags_PadOuterX = 1 << 21, // Default if BordersOuterV is on. Enable outer-most padding. Generally desirable if you have headers. - ImGuiTableFlags_NoPadOuterX = 1 << 22, // Default if BordersOuterV is off. Disable outer-most padding. + ImGuiTableFlags_PadOuterX = 1 << 21, // Default if BordersOuterV is on. Enable outermost padding. Generally desirable if you have headers. + ImGuiTableFlags_NoPadOuterX = 1 << 22, // Default if BordersOuterV is off. Disable outermost padding. ImGuiTableFlags_NoPadInnerX = 1 << 23, // Disable inner padding between columns (double inner padding if BordersOuterV is on, single inner padding if BordersOuterV is off). // Scrolling - ImGuiTableFlags_ScrollX = 1 << 24, // Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Changes default sizing policy. Because this create a child window, ScrollY is currently generally recommended when using ScrollX. + ImGuiTableFlags_ScrollX = 1 << 24, // Enable horizontal scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. Changes default sizing policy. Because this creates a child window, ScrollY is currently generally recommended when using ScrollX. ImGuiTableFlags_ScrollY = 1 << 25, // Enable vertical scrolling. Require 'outer_size' parameter of BeginTable() to specify the container size. // Sorting ImGuiTableFlags_SortMulti = 1 << 26, // Hold shift when clicking headers to sort on multiple column. TableGetSortSpecs() may return specs where (SpecsCount > 1). ImGuiTableFlags_SortTristate = 1 << 27, // Allow no sorting, disable default sorting. TableGetSortSpecs() may return specs where (SpecsCount == 0). // [Internal] Combinations and masks - ImGuiTableFlags_SizingMask_ = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_SizingStretchSame - - // Obsolete names (will be removed soon) -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - //, ImGuiTableFlags_ColumnsWidthFixed = ImGuiTableFlags_SizingFixedFit, ImGuiTableFlags_ColumnsWidthStretch = ImGuiTableFlags_SizingStretchSame // WIP Tables 2020/12 - //, ImGuiTableFlags_SizingPolicyFixed = ImGuiTableFlags_SizingFixedFit, ImGuiTableFlags_SizingPolicyStretch = ImGuiTableFlags_SizingStretchSame // WIP Tables 2021/01 -#endif + ImGuiTableFlags_SizingMask_ = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_SizingStretchSame, }; // Flags for ImGui::TableSetupColumn() @@ -1219,19 +1226,14 @@ enum ImGuiTableColumnFlags_ ImGuiTableColumnFlags_WidthMask_ = ImGuiTableColumnFlags_WidthStretch | ImGuiTableColumnFlags_WidthFixed, ImGuiTableColumnFlags_IndentMask_ = ImGuiTableColumnFlags_IndentEnable | ImGuiTableColumnFlags_IndentDisable, ImGuiTableColumnFlags_StatusMask_ = ImGuiTableColumnFlags_IsEnabled | ImGuiTableColumnFlags_IsVisible | ImGuiTableColumnFlags_IsSorted | ImGuiTableColumnFlags_IsHovered, - ImGuiTableColumnFlags_NoDirectResize_ = 1 << 30 // [Internal] Disable user resizing this column directly (it may however we resized indirectly from its left edge) - - // Obsolete names (will be removed soon) -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - //ImGuiTableColumnFlags_WidthAuto = ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize, // Column will not stretch and keep resizing based on submitted contents. -#endif + ImGuiTableColumnFlags_NoDirectResize_ = 1 << 30, // [Internal] Disable user resizing this column directly (it may however we resized indirectly from its left edge) }; // Flags for ImGui::TableNextRow() enum ImGuiTableRowFlags_ { - ImGuiTableRowFlags_None = 0, - ImGuiTableRowFlags_Headers = 1 << 0 // Identify header row (set default background color + width of its contents accounted differently for auto column width) + ImGuiTableRowFlags_None = 0, + ImGuiTableRowFlags_Headers = 1 << 0, // Identify header row (set default background color + width of its contents accounted differently for auto column width) }; // Enum for ImGui::TableSetBgColor() @@ -1239,16 +1241,16 @@ enum ImGuiTableRowFlags_ // - Layer 0: draw with RowBg0 color if set, otherwise draw with ColumnBg0 if set. // - Layer 1: draw with RowBg1 color if set, otherwise draw with ColumnBg1 if set. // - Layer 2: draw with CellBg color if set. -// The purpose of the two row/columns layers is to let you decide if a background color changes should override or blend with the existing color. +// The purpose of the two row/columns layers is to let you decide if a background color change should override or blend with the existing color. // When using ImGuiTableFlags_RowBg on the table, each row has the RowBg0 color automatically set for odd/even rows. // If you set the color of RowBg0 target, your color will override the existing RowBg0 color. // If you set the color of RowBg1 or ColumnBg1 target, your color will blend over the RowBg0 color. enum ImGuiTableBgTarget_ { - ImGuiTableBgTarget_None = 0, - ImGuiTableBgTarget_RowBg0 = 1, // Set row background color 0 (generally used for background, automatically set when ImGuiTableFlags_RowBg is used) - ImGuiTableBgTarget_RowBg1 = 2, // Set row background color 1 (generally used for selection marking) - ImGuiTableBgTarget_CellBg = 3 // Set cell background color (top-most color) + ImGuiTableBgTarget_None = 0, + ImGuiTableBgTarget_RowBg0 = 1, // Set row background color 0 (generally used for background, automatically set when ImGuiTableFlags_RowBg is used) + ImGuiTableBgTarget_RowBg1 = 2, // Set row background color 1 (generally used for selection marking) + ImGuiTableBgTarget_CellBg = 3, // Set cell background color (top-most color) }; // Flags for ImGui::IsWindowFocused() @@ -1260,7 +1262,7 @@ enum ImGuiFocusedFlags_ ImGuiFocusedFlags_AnyWindow = 1 << 2, // Return true if any window is focused. Important: If you are trying to tell how to dispatch your low-level inputs, do NOT use this. Use 'io.WantCaptureMouse' instead! Please read the FAQ! ImGuiFocusedFlags_NoPopupHierarchy = 1 << 3, // Do not consider popup hierarchy (do not treat popup emitter as parent of popup) (when used with _ChildWindows or _RootWindow) //ImGuiFocusedFlags_DockHierarchy = 1 << 4, // Consider docking hierarchy (treat dockspace host as parent of docked window) (when used with _ChildWindows or _RootWindow) - ImGuiFocusedFlags_RootAndChildWindows = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows + ImGuiFocusedFlags_RootAndChildWindows = ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_ChildWindows, }; // Flags for ImGui::IsItemHovered(), ImGui::IsWindowHovered() @@ -1281,7 +1283,12 @@ enum ImGuiHoveredFlags_ ImGuiHoveredFlags_AllowWhenDisabled = 1 << 9, // IsItemHovered() only: Return true even if the item is disabled ImGuiHoveredFlags_NoNavOverride = 1 << 10, // Disable using gamepad/keyboard navigation state when active, always query mouse. ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped, - ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows + ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows, + + // Hovering delays (for tooltips) + ImGuiHoveredFlags_DelayNormal = 1 << 11, // Return true after io.HoverDelayNormal elapsed (~0.30 sec) + ImGuiHoveredFlags_DelayShort = 1 << 12, // Return true after io.HoverDelayShort elapsed (~0.10 sec) + ImGuiHoveredFlags_NoSharedDelay = 1 << 13, // Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays) }; // Flags for ImGui::BeginDragDropSource(), ImGui::AcceptDragDropPayload() @@ -1289,8 +1296,8 @@ enum ImGuiDragDropFlags_ { ImGuiDragDropFlags_None = 0, // BeginDragDropSource() flags - ImGuiDragDropFlags_SourceNoPreviewTooltip = 1 << 0, // By default, a successful call to BeginDragDropSource opens a tooltip so you can display a preview or description of the source contents. This flag disable this behavior. - ImGuiDragDropFlags_SourceNoDisableHover = 1 << 1, // By default, when dragging we clear data so that IsItemHovered() will return false, to avoid subsequent user code submitting tooltips. This flag disable this behavior so you can still call IsItemHovered() on the source item. + ImGuiDragDropFlags_SourceNoPreviewTooltip = 1 << 0, // Disable preview tooltip. By default, a successful call to BeginDragDropSource opens a tooltip so you can display a preview or description of the source contents. This flag disables this behavior. + ImGuiDragDropFlags_SourceNoDisableHover = 1 << 1, // By default, when dragging we clear data so that IsItemHovered() will return false, to avoid subsequent user code submitting tooltips. This flag disables this behavior so you can still call IsItemHovered() on the source item. ImGuiDragDropFlags_SourceNoHoldToOpenOthers = 1 << 2, // Disable the behavior that allows to open tree nodes and collapsing header by holding over them while dragging a source item. ImGuiDragDropFlags_SourceAllowNullID = 1 << 3, // Allow items such as Text(), Image() that have no unique identifier to be used as drag source, by manufacturing a temporary identifier based on their window-relative position. This is extremely unusual within the dear imgui ecosystem and so we made it explicit. ImGuiDragDropFlags_SourceExtern = 1 << 4, // External source (from outside of dear imgui), won't attempt to read current item/window info. Will always return true. Only one Extern source can be active simultaneously. @@ -1299,7 +1306,7 @@ enum ImGuiDragDropFlags_ ImGuiDragDropFlags_AcceptBeforeDelivery = 1 << 10, // AcceptDragDropPayload() will returns true even before the mouse button is released. You can then call IsDelivery() to test if the payload needs to be delivered. ImGuiDragDropFlags_AcceptNoDrawDefaultRect = 1 << 11, // Do not draw the default highlight rectangle when hovering over target. ImGuiDragDropFlags_AcceptNoPreviewTooltip = 1 << 12, // Request hiding the BeginDragDropSource tooltip from the BeginDragDropTarget site. - ImGuiDragDropFlags_AcceptPeekOnly = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect // For peeking ahead and inspecting the payload before delivery. + ImGuiDragDropFlags_AcceptPeekOnly = ImGuiDragDropFlags_AcceptBeforeDelivery | ImGuiDragDropFlags_AcceptNoDrawDefaultRect, // For peeking ahead and inspecting the payload before delivery. }; // Standard Drag and Drop payload types. You can define you own payload types using short strings. Types starting with '_' are defined by Dear ImGui. @@ -1341,9 +1348,11 @@ enum ImGuiSortDirection_ ImGuiSortDirection_Descending = 2 // Descending = 9->0, Z->A etc. }; -// Keys value 0 to 511 are left unused as legacy native/opaque key values (< 1.87) -// Keys value >= 512 are named keys (>= 1.87) -enum ImGuiKey_ +// A key identifier (ImGuiKey_XXX or ImGuiMod_XXX value): can represent Keyboard, Mouse and Gamepad values. +// All our named keys are >= 512. Keys value 0 to 511 are left unused as legacy native/opaque key values (< 1.87). +// Since >= 1.89 we increased typing (went from int to enum), some legacy code may need a cast to ImGuiKey. +// Read details about the 1.87 and 1.89 transition : https://github.com/ocornut/imgui/issues/4921 +enum ImGuiKey : int { // Keyboard ImGuiKey_None = 0, @@ -1397,113 +1406,94 @@ enum ImGuiKey_ ImGuiKey_KeypadEnter, ImGuiKey_KeypadEqual, - // Gamepad (some of those are analog values, 0.0f to 1.0f) // NAVIGATION action - ImGuiKey_GamepadStart, // Menu (Xbox) + (Switch) Start/Options (PS) // -- - ImGuiKey_GamepadBack, // View (Xbox) - (Switch) Share (PS) // -- - ImGuiKey_GamepadFaceUp, // Y (Xbox) X (Switch) Triangle (PS) // -> ImGuiNavInput_Input - ImGuiKey_GamepadFaceDown, // A (Xbox) B (Switch) Cross (PS) // -> ImGuiNavInput_Activate - ImGuiKey_GamepadFaceLeft, // X (Xbox) Y (Switch) Square (PS) // -> ImGuiNavInput_Menu - ImGuiKey_GamepadFaceRight, // B (Xbox) A (Switch) Circle (PS) // -> ImGuiNavInput_Cancel - ImGuiKey_GamepadDpadUp, // D-pad Up // -> ImGuiNavInput_DpadUp - ImGuiKey_GamepadDpadDown, // D-pad Down // -> ImGuiNavInput_DpadDown - ImGuiKey_GamepadDpadLeft, // D-pad Left // -> ImGuiNavInput_DpadLeft - ImGuiKey_GamepadDpadRight, // D-pad Right // -> ImGuiNavInput_DpadRight - ImGuiKey_GamepadL1, // L Bumper (Xbox) L (Switch) L1 (PS) // -> ImGuiNavInput_FocusPrev + ImGuiNavInput_TweakSlow - ImGuiKey_GamepadR1, // R Bumper (Xbox) R (Switch) R1 (PS) // -> ImGuiNavInput_FocusNext + ImGuiNavInput_TweakFast - ImGuiKey_GamepadL2, // L Trigger (Xbox) ZL (Switch) L2 (PS) [Analog] - ImGuiKey_GamepadR2, // R Trigger (Xbox) ZR (Switch) R2 (PS) [Analog] - ImGuiKey_GamepadL3, // L Thumbstick (Xbox) L3 (Switch) L3 (PS) - ImGuiKey_GamepadR3, // R Thumbstick (Xbox) R3 (Switch) R3 (PS) - ImGuiKey_GamepadLStickUp, // [Analog] // -> ImGuiNavInput_LStickUp - ImGuiKey_GamepadLStickDown, // [Analog] // -> ImGuiNavInput_LStickDown - ImGuiKey_GamepadLStickLeft, // [Analog] // -> ImGuiNavInput_LStickLeft - ImGuiKey_GamepadLStickRight, // [Analog] // -> ImGuiNavInput_LStickRight - ImGuiKey_GamepadRStickUp, // [Analog] - ImGuiKey_GamepadRStickDown, // [Analog] + // Gamepad (some of those are analog values, 0.0f to 1.0f) // NAVIGATION ACTION + // (download controller mapping PNG/PSD at http://dearimgui.org/controls_sheets) + ImGuiKey_GamepadStart, // Menu (Xbox) + (Switch) Start/Options (PS) + ImGuiKey_GamepadBack, // View (Xbox) - (Switch) Share (PS) + ImGuiKey_GamepadFaceLeft, // X (Xbox) Y (Switch) Square (PS) // Tap: Toggle Menu. Hold: Windowing mode (Focus/Move/Resize windows) + ImGuiKey_GamepadFaceRight, // B (Xbox) A (Switch) Circle (PS) // Cancel / Close / Exit + ImGuiKey_GamepadFaceUp, // Y (Xbox) X (Switch) Triangle (PS) // Text Input / On-screen Keyboard + ImGuiKey_GamepadFaceDown, // A (Xbox) B (Switch) Cross (PS) // Activate / Open / Toggle / Tweak + ImGuiKey_GamepadDpadLeft, // D-pad Left // Move / Tweak / Resize Window (in Windowing mode) + ImGuiKey_GamepadDpadRight, // D-pad Right // Move / Tweak / Resize Window (in Windowing mode) + ImGuiKey_GamepadDpadUp, // D-pad Up // Move / Tweak / Resize Window (in Windowing mode) + ImGuiKey_GamepadDpadDown, // D-pad Down // Move / Tweak / Resize Window (in Windowing mode) + ImGuiKey_GamepadL1, // L Bumper (Xbox) L (Switch) L1 (PS) // Tweak Slower / Focus Previous (in Windowing mode) + ImGuiKey_GamepadR1, // R Bumper (Xbox) R (Switch) R1 (PS) // Tweak Faster / Focus Next (in Windowing mode) + ImGuiKey_GamepadL2, // L Trig. (Xbox) ZL (Switch) L2 (PS) [Analog] + ImGuiKey_GamepadR2, // R Trig. (Xbox) ZR (Switch) R2 (PS) [Analog] + ImGuiKey_GamepadL3, // L Stick (Xbox) L3 (Switch) L3 (PS) + ImGuiKey_GamepadR3, // R Stick (Xbox) R3 (Switch) R3 (PS) + ImGuiKey_GamepadLStickLeft, // [Analog] // Move Window (in Windowing mode) + ImGuiKey_GamepadLStickRight, // [Analog] // Move Window (in Windowing mode) + ImGuiKey_GamepadLStickUp, // [Analog] // Move Window (in Windowing mode) + ImGuiKey_GamepadLStickDown, // [Analog] // Move Window (in Windowing mode) ImGuiKey_GamepadRStickLeft, // [Analog] ImGuiKey_GamepadRStickRight, // [Analog] + ImGuiKey_GamepadRStickUp, // [Analog] + ImGuiKey_GamepadRStickDown, // [Analog] + + // Aliases: Mouse Buttons (auto-submitted from AddMouseButtonEvent() calls) + // - This is mirroring the data also written to io.MouseDown[], io.MouseWheel, in a format allowing them to be accessed via standard key API. + ImGuiKey_MouseLeft, ImGuiKey_MouseRight, ImGuiKey_MouseMiddle, ImGuiKey_MouseX1, ImGuiKey_MouseX2, ImGuiKey_MouseWheelX, ImGuiKey_MouseWheelY, + + // [Internal] Reserved for mod storage + ImGuiKey_ReservedForModCtrl, ImGuiKey_ReservedForModShift, ImGuiKey_ReservedForModAlt, ImGuiKey_ReservedForModSuper, + ImGuiKey_COUNT, // Keyboard Modifiers (explicitly submitted by backend via AddKeyEvent() calls) // - This is mirroring the data also written to io.KeyCtrl, io.KeyShift, io.KeyAlt, io.KeySuper, in a format allowing // them to be accessed via standard key API, allowing calls such as IsKeyPressed(), IsKeyReleased(), querying duration etc. - // - Code polling every keys (e.g. an interface to detect a key press for input mapping) might want to ignore those - // and prefer using the real keys (e.g. ImGuiKey_LeftCtrl, ImGuiKey_RightCtrl instead of ImGuiKey_ModCtrl). + // - Code polling every key (e.g. an interface to detect a key press for input mapping) might want to ignore those + // and prefer using the real keys (e.g. ImGuiKey_LeftCtrl, ImGuiKey_RightCtrl instead of ImGuiMod_Ctrl). // - In theory the value of keyboard modifiers should be roughly equivalent to a logical or of the equivalent left/right keys. // In practice: it's complicated; mods are often provided from different sources. Keyboard layout, IME, sticky keys and // backends tend to interfere and break that equivalence. The safer decision is to relay that ambiguity down to the end-user... - ImGuiKey_ModCtrl, ImGuiKey_ModShift, ImGuiKey_ModAlt, ImGuiKey_ModSuper, + ImGuiMod_None = 0, + ImGuiMod_Ctrl = 1 << 12, // Ctrl + ImGuiMod_Shift = 1 << 13, // Shift + ImGuiMod_Alt = 1 << 14, // Option/Menu + ImGuiMod_Super = 1 << 15, // Cmd/Super/Windows + ImGuiMod_Shortcut = 1 << 11, // Alias for Ctrl (non-macOS) _or_ Super (macOS). + ImGuiMod_Mask_ = 0xF800, // 5-bits - // End of list - ImGuiKey_COUNT, // No valid ImGuiKey is ever greater than this value - - // [Internal] Prior to 1.87 we required user to fill io.KeysDown[512] using their own native index + a io.KeyMap[] array. + // [Internal] Prior to 1.87 we required user to fill io.KeysDown[512] using their own native index + the io.KeyMap[] array. // We are ditching this method but keeping a legacy path for user code doing e.g. IsKeyPressed(MY_NATIVE_KEY_CODE) ImGuiKey_NamedKey_BEGIN = 512, ImGuiKey_NamedKey_END = ImGuiKey_COUNT, ImGuiKey_NamedKey_COUNT = ImGuiKey_NamedKey_END - ImGuiKey_NamedKey_BEGIN, #ifdef IMGUI_DISABLE_OBSOLETE_KEYIO ImGuiKey_KeysData_SIZE = ImGuiKey_NamedKey_COUNT, // Size of KeysData[]: only hold named keys - ImGuiKey_KeysData_OFFSET = ImGuiKey_NamedKey_BEGIN // First key stored in io.KeysData[0]. Accesses to io.KeysData[] must use (key - ImGuiKey_KeysData_OFFSET). + ImGuiKey_KeysData_OFFSET = ImGuiKey_NamedKey_BEGIN, // First key stored in io.KeysData[0]. Accesses to io.KeysData[] must use (key - ImGuiKey_KeysData_OFFSET). #else ImGuiKey_KeysData_SIZE = ImGuiKey_COUNT, // Size of KeysData[]: hold legacy 0..512 keycodes + named keys - ImGuiKey_KeysData_OFFSET = 0 // First key stored in io.KeysData[0]. Accesses to io.KeysData[] must use (key - ImGuiKey_KeysData_OFFSET). + ImGuiKey_KeysData_OFFSET = 0, // First key stored in io.KeysData[0]. Accesses to io.KeysData[] must use (key - ImGuiKey_KeysData_OFFSET). #endif #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - , ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter // Renamed in 1.87 + ImGuiKey_ModCtrl = ImGuiMod_Ctrl, ImGuiKey_ModShift = ImGuiMod_Shift, ImGuiKey_ModAlt = ImGuiMod_Alt, ImGuiKey_ModSuper = ImGuiMod_Super, // Renamed in 1.89 + ImGuiKey_KeyPadEnter = ImGuiKey_KeypadEnter, // Renamed in 1.87 #endif }; -// Helper "flags" version of key-mods to store and compare multiple key-mods easily. Sometimes used for storage (e.g. io.KeyMods) but otherwise not much used in public API. -enum ImGuiModFlags_ +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO +// OBSOLETED in 1.88 (from July 2022): ImGuiNavInput and io.NavInputs[]. +// Official backends between 1.60 and 1.86: will keep working and feed gamepad inputs as long as IMGUI_DISABLE_OBSOLETE_KEYIO is not set. +// Custom backends: feed gamepad inputs via io.AddKeyEvent() and ImGuiKey_GamepadXXX enums. +enum ImGuiNavInput { - ImGuiModFlags_None = 0, - ImGuiModFlags_Ctrl = 1 << 0, - ImGuiModFlags_Shift = 1 << 1, - ImGuiModFlags_Alt = 1 << 2, // Menu - ImGuiModFlags_Super = 1 << 3 // Cmd/Super/Windows key -}; - -// Gamepad/Keyboard navigation -// Since >= 1.87 backends you generally don't need to care about this enum since io.NavInputs[] is setup automatically. This might become private/internal some day. -// Keyboard: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard to enable. NewFrame() will automatically fill io.NavInputs[] based on your io.AddKeyEvent() calls. -// Gamepad: Set io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad to enable. Backend: set ImGuiBackendFlags_HasGamepad and fill the io.NavInputs[] fields before calling NewFrame(). Note that io.NavInputs[] is cleared by EndFrame(). -// Read instructions in imgui.cpp for more details. Download PNG/PSD at http://dearimgui.org/controls_sheets. -enum ImGuiNavInput_ -{ - // Gamepad Mapping - ImGuiNavInput_Activate, // Activate / Open / Toggle / Tweak value // e.g. Cross (PS4), A (Xbox), A (Switch), Space (Keyboard) - ImGuiNavInput_Cancel, // Cancel / Close / Exit // e.g. Circle (PS4), B (Xbox), B (Switch), Escape (Keyboard) - ImGuiNavInput_Input, // Text input / On-Screen keyboard // e.g. Triang.(PS4), Y (Xbox), X (Switch), Return (Keyboard) - ImGuiNavInput_Menu, // Tap: Toggle menu / Hold: Focus, Move, Resize // e.g. Square (PS4), X (Xbox), Y (Switch), Alt (Keyboard) - ImGuiNavInput_DpadLeft, // Move / Tweak / Resize window (w/ PadMenu) // e.g. D-pad Left/Right/Up/Down (Gamepads), Arrow keys (Keyboard) - ImGuiNavInput_DpadRight, // - ImGuiNavInput_DpadUp, // - ImGuiNavInput_DpadDown, // - ImGuiNavInput_LStickLeft, // Scroll / Move window (w/ PadMenu) // e.g. Left Analog Stick Left/Right/Up/Down - ImGuiNavInput_LStickRight, // - ImGuiNavInput_LStickUp, // - ImGuiNavInput_LStickDown, // - ImGuiNavInput_FocusPrev, // Focus Next window (w/ PadMenu) // e.g. L1 or L2 (PS4), LB or LT (Xbox), L or ZL (Switch) - ImGuiNavInput_FocusNext, // Focus Prev window (w/ PadMenu) // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch) - ImGuiNavInput_TweakSlow, // Slower tweaks // e.g. L1 or L2 (PS4), LB or LT (Xbox), L or ZL (Switch) - ImGuiNavInput_TweakFast, // Faster tweaks // e.g. R1 or R2 (PS4), RB or RT (Xbox), R or ZL (Switch) - - // [Internal] Don't use directly! This is used internally to differentiate keyboard from gamepad inputs for behaviors that require to differentiate them. - // Keyboard behavior that have no corresponding gamepad mapping (e.g. CTRL+TAB) will be directly reading from keyboard keys instead of io.NavInputs[]. - ImGuiNavInput_KeyLeft_, // Move left // = Arrow keys - ImGuiNavInput_KeyRight_, // Move right - ImGuiNavInput_KeyUp_, // Move up - ImGuiNavInput_KeyDown_, // Move down - ImGuiNavInput_COUNT + ImGuiNavInput_Activate, ImGuiNavInput_Cancel, ImGuiNavInput_Input, ImGuiNavInput_Menu, ImGuiNavInput_DpadLeft, ImGuiNavInput_DpadRight, ImGuiNavInput_DpadUp, ImGuiNavInput_DpadDown, + ImGuiNavInput_LStickLeft, ImGuiNavInput_LStickRight, ImGuiNavInput_LStickUp, ImGuiNavInput_LStickDown, ImGuiNavInput_FocusPrev, ImGuiNavInput_FocusNext, ImGuiNavInput_TweakSlow, ImGuiNavInput_TweakFast, + ImGuiNavInput_COUNT, }; +#endif // Configuration flags stored in io.ConfigFlags. Set by user/application. enum ImGuiConfigFlags_ { ImGuiConfigFlags_None = 0, - ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. NewFrame() will automatically fill io.NavInputs[] based on io.AddKeyEvent() calls - ImGuiConfigFlags_NavEnableGamepad = 1 << 1, // Master gamepad navigation enable flag. This is mostly to instruct your imgui backend to fill io.NavInputs[]. Backend also needs to set ImGuiBackendFlags_HasGamepad. + ImGuiConfigFlags_NavEnableKeyboard = 1 << 0, // Master keyboard navigation enable flag. + ImGuiConfigFlags_NavEnableGamepad = 1 << 1, // Master gamepad navigation enable flag. Backend also needs to set ImGuiBackendFlags_HasGamepad. ImGuiConfigFlags_NavEnableSetMousePos = 1 << 2, // Instruct navigation to move the mouse cursor. May be useful on TV/console systems where moving a virtual mouse is awkward. Will update io.MousePos and set io.WantSetMousePos=true. If enabled you MUST honor io.WantSetMousePos requests in your backend, otherwise ImGui will react as if the mouse is jumping around back and forth. ImGuiConfigFlags_NavNoCaptureKeyboard = 1 << 3, // Instruct navigation to not set the io.WantCaptureKeyboard flag when io.NavActive is set. ImGuiConfigFlags_NoMouse = 1 << 4, // Instruct imgui to clear mouse position/buttons in NewFrame(). This allows ignoring the mouse information set by the backend. @@ -1511,7 +1501,7 @@ enum ImGuiConfigFlags_ // User storage (to allow your backend/engine to communicate to code that may be shared between multiple projects. Those flags are NOT used by core Dear ImGui) ImGuiConfigFlags_IsSRGB = 1 << 20, // Application is SRGB-aware. - ImGuiConfigFlags_IsTouchScreen = 1 << 21 // Application is using a touch screen instead of a mouse. + ImGuiConfigFlags_IsTouchScreen = 1 << 21, // Application is using a touch screen instead of a mouse. }; // Backend capabilities flags stored in io.BackendFlags. Set by imgui_impl_xxx or custom backend. @@ -1521,7 +1511,7 @@ enum ImGuiBackendFlags_ ImGuiBackendFlags_HasGamepad = 1 << 0, // Backend Platform supports gamepad and currently has one connected. ImGuiBackendFlags_HasMouseCursors = 1 << 1, // Backend Platform supports honoring GetMouseCursor() value to change the OS cursor shape. ImGuiBackendFlags_HasSetMousePos = 1 << 2, // Backend Platform supports io.WantSetMousePos requests to reposition the OS mouse position (only used if ImGuiConfigFlags_NavEnableSetMousePos is set). - ImGuiBackendFlags_RendererHasVtxOffset = 1 << 3 // Backend Renderer supports ImDrawCmd::VtxOffset. This enables output of large meshes (64K+ vertices) while still using 16-bit indices. + ImGuiBackendFlags_RendererHasVtxOffset = 1 << 3, // Backend Renderer supports ImDrawCmd::VtxOffset. This enables output of large meshes (64K+ vertices) while still using 16-bit indices. }; // Enumeration for PushStyleColor() / PopStyleColor() @@ -1631,7 +1621,7 @@ enum ImGuiButtonFlags_ // [Internal] ImGuiButtonFlags_MouseButtonMask_ = ImGuiButtonFlags_MouseButtonLeft | ImGuiButtonFlags_MouseButtonRight | ImGuiButtonFlags_MouseButtonMiddle, - ImGuiButtonFlags_MouseButtonDefault_ = ImGuiButtonFlags_MouseButtonLeft + ImGuiButtonFlags_MouseButtonDefault_ = ImGuiButtonFlags_MouseButtonLeft, }; // Flags for ColorEdit3() / ColorEdit4() / ColorPicker3() / ColorPicker4() / ColorButton() @@ -1672,7 +1662,7 @@ enum ImGuiColorEditFlags_ ImGuiColorEditFlags_DisplayMask_ = ImGuiColorEditFlags_DisplayRGB | ImGuiColorEditFlags_DisplayHSV | ImGuiColorEditFlags_DisplayHex, ImGuiColorEditFlags_DataTypeMask_ = ImGuiColorEditFlags_Uint8 | ImGuiColorEditFlags_Float, ImGuiColorEditFlags_PickerMask_ = ImGuiColorEditFlags_PickerHueWheel | ImGuiColorEditFlags_PickerHueBar, - ImGuiColorEditFlags_InputMask_ = ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_InputHSV + ImGuiColorEditFlags_InputMask_ = ImGuiColorEditFlags_InputRGB | ImGuiColorEditFlags_InputHSV, // Obsolete names (will be removed) // ImGuiColorEditFlags_RGB = ImGuiColorEditFlags_DisplayRGB, ImGuiColorEditFlags_HSV = ImGuiColorEditFlags_DisplayHSV, ImGuiColorEditFlags_HEX = ImGuiColorEditFlags_DisplayHex // [renamed in 1.69] @@ -1680,6 +1670,7 @@ enum ImGuiColorEditFlags_ // Flags for DragFloat(), DragInt(), SliderFloat(), SliderInt() etc. // We use the same sets of flags for DragXXX() and SliderXXX() functions as the features are the same and it makes it easier to swap them. +// (Those are per-item flags. There are shared flags in ImGuiIO: io.ConfigDragClickToInputText) enum ImGuiSliderFlags_ { ImGuiSliderFlags_None = 0, @@ -1687,11 +1678,11 @@ enum ImGuiSliderFlags_ ImGuiSliderFlags_Logarithmic = 1 << 5, // Make the widget logarithmic (linear otherwise). Consider using ImGuiSliderFlags_NoRoundToFormat with this if using a format-string with small amount of digits. ImGuiSliderFlags_NoRoundToFormat = 1 << 6, // Disable rounding underlying value to match precision of the display format string (e.g. %.3f values are rounded to those 3 digits) ImGuiSliderFlags_NoInput = 1 << 7, // Disable CTRL+Click or Enter key allowing to input text directly into the widget - ImGuiSliderFlags_InvalidMask_ = 0x7000000F // [Internal] We treat using those bits as being potentially a 'float power' argument from the previous API that has got miscast to this enum, and will trigger an assert if needed. + ImGuiSliderFlags_InvalidMask_ = 0x7000000F, // [Internal] We treat using those bits as being potentially a 'float power' argument from the previous API that has got miscast to this enum, and will trigger an assert if needed. // Obsolete names (will be removed) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - , ImGuiSliderFlags_ClampOnInput = ImGuiSliderFlags_AlwaysClamp // [renamed in 1.79] + ImGuiSliderFlags_ClampOnInput = ImGuiSliderFlags_AlwaysClamp, // [renamed in 1.79] #endif }; @@ -1713,7 +1704,7 @@ enum ImGuiMouseCursor_ ImGuiMouseCursor_Arrow = 0, ImGuiMouseCursor_TextInput, // When hovering over InputText, etc. ImGuiMouseCursor_ResizeAll, // (Unused by Dear ImGui functions) - ImGuiMouseCursor_ResizeNS, // When hovering over an horizontal border + ImGuiMouseCursor_ResizeNS, // When hovering over a horizontal border ImGuiMouseCursor_ResizeEW, // When hovering over a vertical border or a column ImGuiMouseCursor_ResizeNESW, // When hovering over the bottom-left corner of a window ImGuiMouseCursor_ResizeNWSE, // When hovering over the bottom-right corner of a window @@ -1728,10 +1719,10 @@ enum ImGuiMouseCursor_ enum ImGuiCond_ { ImGuiCond_None = 0, // No condition (always set the variable), same as _Always - ImGuiCond_Always = 1 << 0, // No condition (always set the variable) + ImGuiCond_Always = 1 << 0, // No condition (always set the variable), same as _None ImGuiCond_Once = 1 << 1, // Set the variable once per runtime session (only the first call will succeed) ImGuiCond_FirstUseEver = 1 << 2, // Set the variable if the object/window has no persistently saved data (no entry in .ini file) - ImGuiCond_Appearing = 1 << 3 // Set the variable if the object/window is appearing after being hidden/inactive (or the first time) + ImGuiCond_Appearing = 1 << 3, // Set the variable if the object/window is appearing after being hidden/inactive (or the first time) }; //----------------------------------------------------------------------------- @@ -1780,7 +1771,7 @@ struct ImVector // Constructors, destructor inline ImVector() { Size = Capacity = 0; Data = NULL; } inline ImVector(const ImVector& src) { Size = Capacity = 0; Data = NULL; operator=(src); } - inline ImVector& operator=(const ImVector& src) { clear(); resize(src.Size); memcpy(Data, src.Data, (size_t)Size * sizeof(T)); return *this; } + inline ImVector& operator=(const ImVector& src) { clear(); resize(src.Size); if (src.Data) memcpy(Data, src.Data, (size_t)Size * sizeof(T)); return *this; } inline ~ImVector() { if (Data) IM_FREE(Data); } // Important: does not destruct anything inline void clear() { if (Data) { Size = Capacity = 0; IM_FREE(Data); Data = NULL; } } // Important: does not destruct anything @@ -1844,7 +1835,7 @@ struct ImGuiStyle ImVec2 WindowPadding; // Padding within a window. float WindowRounding; // Radius of window corners rounding. Set to 0.0f to have rectangular windows. Large values tend to lead to variety of artifacts and are not recommended. float WindowBorderSize; // Thickness of border around windows. Generally set to 0.0f or 1.0f. (Other values are not well tested and more CPU/GPU costly). - ImVec2 WindowMinSize; // Minimum window size. This is a global setting. If you want to constraint individual windows, use SetNextWindowSizeConstraints(). + ImVec2 WindowMinSize; // Minimum window size. This is a global setting. If you want to constrain individual windows, use SetNextWindowSizeConstraints(). ImVec2 WindowTitleAlign; // Alignment for title bar text. Defaults to (0.0f,0.5f) for left-aligned,vertically centered. ImGuiDir WindowMenuButtonPosition; // Side of the collapsing/docking button in the title bar (None/Left/Right). Defaults to ImGuiDir_Left. float ChildRounding; // Radius of child window corners rounding. Set to 0.0f to have rectangular windows. @@ -1867,7 +1858,7 @@ struct ImGuiStyle float LogSliderDeadzone; // The size in pixels of the dead-zone around zero on logarithmic sliders that cross zero. float TabRounding; // Radius of upper corners of a tab. Set to 0.0f to have rectangular tabs. float TabBorderSize; // Thickness of border around tabs. - float TabMinWidthForCloseButton; // Minimum width for close button to appears on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected. + float TabMinWidthForCloseButton; // Minimum width for close button to appear on an unselected tab when hovered. Set to 0.0f to always show when hovering, set to FLT_MAX to never show close button unless selected. ImGuiDir ColorButtonPosition; // Side of the color button in the ColorEdit4 widget (left/right). Defaults to ImGuiDir_Right. ImVec2 ButtonTextAlign; // Alignment of button text when button is larger than text. Defaults to (0.5f, 0.5f) (centered). ImVec2 SelectableTextAlign; // Alignment of selectable text. Defaults to (0.0f, 0.0f) (top-left aligned). It's generally important to keep this left-aligned if you want to lay multiple items on a same line. @@ -1893,7 +1884,7 @@ struct ImGuiStyle //----------------------------------------------------------------------------- // [Internal] Storage used by IsKeyDown(), IsKeyPressed() etc functions. -// If prior to 1.87 you used io.KeysDownDuration[] (which was marked as internal), you should use GetKeyData(key)->DownDuration and not io.KeysData[key]->DownDuration. +// If prior to 1.87 you used io.KeysDownDuration[] (which was marked as internal), you should use GetKeyData(key)->DownDuration and *NOT* io.KeysData[key]->DownDuration. struct ImGuiKeyData { bool Down; // True for if key is down @@ -1918,9 +1909,11 @@ struct ImGuiIO float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds. float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels. float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging. - float KeyRepeatDelay; // = 0.250f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.). + float KeyRepeatDelay; // = 0.275f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.). float KeyRepeatRate; // = 0.050f // When holding a key/button, rate at which it repeats, in seconds. - void* UserData; // = NULL // Store your own data for retrieval by callbacks. + float HoverDelayNormal; // = 0.30 sec // Delay on hovering before IsItemHovered(ImGuiHoveredFlags_DelayNormal) returns true. + float HoverDelayShort; // = 0.10 sec // Delay on hovering before IsItemHovered(ImGuiHoveredFlags_DelayShort) returns true. + void* UserData; // = NULL // Store your own data. ImFontAtlas*Fonts; // // Font atlas: load, rasterize and pack one or more fonts into a single texture. float FontGlobalScale; // = 1.0f // Global scale all fonts @@ -1933,6 +1926,7 @@ struct ImGuiIO bool ConfigMacOSXBehaviors; // = defined(__APPLE__) // OS X style: Text editing cursor movement using Alt instead of Ctrl, Shortcuts using Cmd/Super instead of Ctrl, Line/Text Start and End using Cmd+Arrows instead of Home/End, Double click selects by word instead of selecting whole text, Multi-selection in lists uses Cmd/Super instead of Ctrl. bool ConfigInputTrickleEventQueue; // = true // Enable input queue trickling: some types of events submitted during the same frame (e.g. button down + up) will be spread over multiple frames, improving interactions with low framerates. bool ConfigInputTextCursorBlink; // = true // Enable blinking cursor (optional as some users consider it to be distracting). + bool ConfigInputTextEnterKeepActive; // = false // [BETA] Pressing Enter will keep item active and select contents (single-line only). bool ConfigDragClickToInputText; // = false // [BETA] Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving). Not desirable on devices without a keyboard. bool ConfigWindowsResizeFromEdges; // = true // Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag) bool ConfigWindowsMoveFromTitleBarOnly; // = false // Enable allowing to move windows only when clicking on their title bar. Does not apply to windows without a title bar. @@ -1977,8 +1971,8 @@ struct ImGuiIO IMGUI_API void AddMouseWheelEvent(float wh_x, float wh_y); // Queue a mouse wheel update IMGUI_API void AddFocusEvent(bool focused); // Queue a gain/loss of focus for the application (generally based on OS/platform focus of your window) IMGUI_API void AddInputCharacter(unsigned int c); // Queue a new character input - IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue a new character input from an UTF-16 character, it can be a surrogate - IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue a new characters input from an UTF-8 string + IMGUI_API void AddInputCharacterUTF16(ImWchar16 c); // Queue a new character input from a UTF-16 character, it can be a surrogate + IMGUI_API void AddInputCharactersUTF8(const char* str); // Queue a new characters input from a UTF-8 string IMGUI_API void SetKeyEventNativeData(ImGuiKey key, int native_keycode, int native_scancode, int native_legacy_index = -1); // [Optional] Specify index for legacy <1.87 IsKeyXXX() functions with native indices + specify native keycode, scancode. IMGUI_API void SetAppAcceptingEvents(bool accepting_events); // Set master flag for accepting key/mouse/text events (default to true). Useful if you have native dialog boxes that are interrupting your application loop/refresh, and you want to disable events being queued while your app is frozen. @@ -1998,7 +1992,7 @@ struct ImGuiIO bool WantSaveIniSettings; // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. Important: clear io.WantSaveIniSettings yourself after saving! bool NavActive; // Keyboard/Gamepad navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag. bool NavVisible; // Keyboard/Gamepad navigation is visible and allowed (will handle ImGuiKey_NavXXX events). - float Framerate; // Rough estimate of application framerate, in frame per second. Solely for convenience. Rolling average estimation based on io.DeltaTime over 120 frames. + float Framerate; // Estimate of application framerate (rolling average over 60 frames, based on io.DeltaTime), in frame per second. Solely for convenience. Slow applications may not want to use a moving average or may want to reset underlying buffers occasionally. int MetricsRenderVertices; // Vertices output during last call to Render() int MetricsRenderIndices; // Indices output during last call to Render() = number of triangles * 3 int MetricsRenderWindows; // Number of visible windows @@ -2008,9 +2002,11 @@ struct ImGuiIO // Legacy: before 1.87, we required backend to fill io.KeyMap[] (imgui->native map) during initialization and io.KeysDown[] (native indices) every frame. // This is still temporarily supported as a legacy feature. However the new preferred scheme is for backend to call io.AddKeyEvent(). + // Old (<1.87): ImGui::IsKeyPressed(ImGui::GetIO().KeyMap[ImGuiKey_Space]) --> New (1.87+) ImGui::IsKeyPressed(ImGuiKey_Space) #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO int KeyMap[ImGuiKey_COUNT]; // [LEGACY] Input: map of indices into the KeysDown[512] entries array which represent your "native" keyboard state. The first 512 are now unused and should be kept zero. Legacy backend will write into KeyMap[] using ImGuiKey_ indices which are always >512. bool KeysDown[ImGuiKey_COUNT]; // [LEGACY] Input: Keyboard keys that are pressed (ideally left in the "native" order your engine has access to keyboard keys, so you can use your own defines/enums for keys). This used to be [512] sized. It is now ImGuiKey_COUNT to allow legacy io.KeysDown[GetKeyIndex(...)] to work without an overflow. + float NavInputs[ImGuiNavInput_COUNT]; // [LEGACY] Since 1.88, NavInputs[] was removed. Backends from 1.60 to 1.86 won't build. Feed gamepad inputs via io.AddKeyEvent() and ImGuiKey_GamepadXXX enums. #endif //------------------------------------------------------------------ @@ -2021,18 +2017,17 @@ struct ImGuiIO // (this block used to be written by backend, since 1.87 it is best to NOT write to those directly, call the AddXXX functions above instead) // (reading from those variables is fair game, as they are extremely unlikely to be moving anywhere) ImVec2 MousePos; // Mouse position, in pixels. Set to ImVec2(-FLT_MAX, -FLT_MAX) if mouse is unavailable (on another screen, etc.) - bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras (ImGuiMouseButton_COUNT == 5). Dear ImGui mostly uses left and right buttons. Others buttons allows us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. + bool MouseDown[5]; // Mouse buttons: 0=left, 1=right, 2=middle + extras (ImGuiMouseButton_COUNT == 5). Dear ImGui mostly uses left and right buttons. Other buttons allow us to track if the mouse is being used by your application + available to user as a convenience via IsMouse** API. float MouseWheel; // Mouse wheel Vertical: 1 unit scrolls about 5 lines text. - float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with an horizontal wheel, may not be filled by all backends. + float MouseWheelH; // Mouse wheel Horizontal. Most users don't have a mouse with a horizontal wheel, may not be filled by all backends. bool KeyCtrl; // Keyboard modifier down: Control bool KeyShift; // Keyboard modifier down: Shift bool KeyAlt; // Keyboard modifier down: Alt bool KeySuper; // Keyboard modifier down: Cmd/Super/Windows - float NavInputs[ImGuiNavInput_COUNT]; // Gamepad inputs. Cleared back to zero by EndFrame(). Keyboard keys will be auto-mapped and be written here by NewFrame(). // Other state maintained from data above + IO function calls - ImGuiModFlags KeyMods; // Key mods flags (same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags), updated by NewFrame() - ImGuiKeyData KeysData[ImGuiKey_KeysData_SIZE]; // Key state for all known keys. Use IsKeyXXX() functions to access this. + ImGuiKeyChord KeyMods; // Key mods flags (any of ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Alt/ImGuiMod_Super flags, same as io.KeyCtrl/KeyShift/KeyAlt/KeySuper but merged into flags. DOES NOT CONTAINS ImGuiMod_Shortcut which is pretranslated). Read-only, updated by NewFrame() + ImGuiKeyData KeysData[ImGuiKey_KeysData_SIZE]; // Key state for all known keys. Use IsKeyXXX() functions to access this. bool WantCaptureMouseUnlessPopupClose; // Alternative to WantCaptureMouse: (WantCaptureMouse == true && WantCaptureMouseUnlessPopupClose == false) when a click over void is expected to close a popup. ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid) ImVec2 MouseClickedPos[5]; // Position at time of clicking @@ -2047,8 +2042,6 @@ struct ImGuiIO float MouseDownDuration[5]; // Duration the mouse button has been down (0.0f == just clicked) float MouseDownDurationPrev[5]; // Previous time the mouse button has been down float MouseDragMaxDistanceSqr[5]; // Squared maximum distance of how much mouse has traveled from the clicking point (used for moving thresholds) - float NavInputsDownDuration[ImGuiNavInput_COUNT]; - float NavInputsDownDurationPrev[ImGuiNavInput_COUNT]; float PenPressure; // Touch/Pen pressure (0.0f to 1.0f, should be >0.0f only when MouseDown[0] == true). Helper storage currently unused by Dear ImGui. bool AppFocusLost; // Only modify via AddFocusEvent() bool AppAcceptingEvents; // Only modify via SetAppAcceptingEvents() @@ -2106,7 +2099,7 @@ struct ImGuiInputTextCallbackData // NB: For basic min/max size constraint on each axis you don't need to use the callback! The SetNextWindowSizeConstraints() parameters are enough. struct ImGuiSizeCallbackData { - void* UserData; // Read-only. What user passed to SetNextWindowSizeConstraints() + void* UserData; // Read-only. What user passed to SetNextWindowSizeConstraints(). Generally store an integer or float in here (need reinterpret_cast<>). ImVec2 Pos; // Read-only. Window position, for reference. ImVec2 CurrentSize; // Read-only. Current window size. ImVec2 DesiredSize; // Read-write. Desired size, based on user's mouse position. Write to this field to restrain resizing. @@ -2170,7 +2163,7 @@ struct ImGuiTableSortSpecs #define IM_UNICODE_CODEPOINT_MAX 0xFFFF // Maximum Unicode code point supported by this build. #endif -// Helper: Execute a block of code at maximum once a frame. Convenient if you want to quickly create an UI within deep-nested code that runs multiple times every frame. +// Helper: Execute a block of code at maximum once a frame. Convenient if you want to quickly create a UI within deep-nested code that runs multiple times every frame. // Usage: static ImGuiOnceUponAFrame oaf; if (oaf) ImGui::Text("This will be called only once per frame"); struct ImGuiOnceUponAFrame { @@ -2278,7 +2271,7 @@ struct ImGuiStorage }; // Helper: Manually clip large list of items. -// If you have lots evenly spaced items and you have a random access to the list, you can perform coarse +// If you have lots evenly spaced items and you have random access to the list, you can perform coarse // clipping based on visibility to only submit items that are in view. // The clipper calculates the range of visible items and advance the cursor to compensate for the non-visible items we have skipped. // (Dear ImGui already clip items based on their bounds but: it needs to first layout the item to do so, and generally @@ -2424,7 +2417,7 @@ struct ImDrawVert #else // You can override the vertex format layout by defining IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT in imconfig.h // The code expect ImVec2 pos (8 bytes), ImVec2 uv (8 bytes), ImU32 col (4 bytes), but you can re-order them or add other fields as needed to simplify integration in your engine. -// The type has to be described within the macro (you can either declare the struct or use a typedef). This is because ImVec2/ImU32 are likely not declared a the time you'd want to set your type up. +// The type has to be described within the macro (you can either declare the struct or use a typedef). This is because ImVec2/ImU32 are likely not declared at the time you'd want to set your type up. // NOTE: IMGUI DOESN'T CLEAR THE STRUCTURE AND DOESN'T CALL A CONSTRUCTOR SO ANY CUSTOM FIELD WILL BE UNINITIALIZED. IF YOU ADD EXTRA FIELDS (SUCH AS A 'Z' COORDINATES) YOU WILL NEED TO CLEAR THEM DURING RENDER OR TO IGNORE THEM. IMGUI_OVERRIDE_DRAWVERT_STRUCT_LAYOUT; #endif @@ -2479,7 +2472,7 @@ enum ImDrawFlags_ ImDrawFlags_RoundCornersRight = ImDrawFlags_RoundCornersBottomRight | ImDrawFlags_RoundCornersTopRight, ImDrawFlags_RoundCornersAll = ImDrawFlags_RoundCornersTopLeft | ImDrawFlags_RoundCornersTopRight | ImDrawFlags_RoundCornersBottomLeft | ImDrawFlags_RoundCornersBottomRight, ImDrawFlags_RoundCornersDefault_ = ImDrawFlags_RoundCornersAll, // Default to ALL corners if none of the _RoundCornersXX flags are specified. - ImDrawFlags_RoundCornersMask_ = ImDrawFlags_RoundCornersAll | ImDrawFlags_RoundCornersNone + ImDrawFlags_RoundCornersMask_ = ImDrawFlags_RoundCornersAll | ImDrawFlags_RoundCornersNone, }; // Flags for ImDrawList instance. Those are set automatically by ImGui:: functions from ImGuiIO settings, and generally not manipulated directly. @@ -2490,7 +2483,7 @@ enum ImDrawListFlags_ ImDrawListFlags_AntiAliasedLines = 1 << 0, // Enable anti-aliased lines/borders (*2 the number of triangles for 1.0f wide line or lines thin enough to be drawn using textures, otherwise *3 the number of triangles) ImDrawListFlags_AntiAliasedLinesUseTex = 1 << 1, // Enable anti-aliased lines/borders using textures when possible. Require backend to render with bilinear filtering (NOT point/nearest filtering). ImDrawListFlags_AntiAliasedFill = 1 << 2, // Enable anti-aliased edge around filled shapes (rounded rectangles, circles). - ImDrawListFlags_AllowVtxOffset = 1 << 3 // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled. + ImDrawListFlags_AllowVtxOffset = 1 << 3, // Can emit 'VtxOffset > 0' to allow large meshes. Set when 'ImGuiBackendFlags_RendererHasVtxOffset' is enabled. }; // Draw command list @@ -2512,7 +2505,7 @@ struct ImDrawList // [Internal, used while building lists] unsigned int _VtxCurrentIdx; // [Internal] generally == VtxBuffer.Size unless we are past 64K vertices, in which case this gets reset to 0. - const ImDrawListSharedData* _Data; // Pointer to shared draw data (you can use ImGui::GetDrawListSharedData() to get the one from current ImGui context) + ImDrawListSharedData* _Data; // Pointer to shared draw data (you can use ImGui::GetDrawListSharedData() to get the one from current ImGui context) const char* _OwnerName; // Pointer to owner window's name for debugging ImDrawVert* _VtxWritePtr; // [Internal] point within VtxBuffer.Data after each add command (to avoid using the ImVector<> operators too much) ImDrawIdx* _IdxWritePtr; // [Internal] point within IdxBuffer.Data after each add command (to avoid using the ImVector<> operators too much) @@ -2524,7 +2517,7 @@ struct ImDrawList float _FringeScale; // [Internal] anti-alias fringe is scaled by this value, this helps to keep things sharp while zooming at vertex buffer content // If you want to create ImDrawList instances, pass them ImGui::GetDrawListSharedData() or create and use your own ImDrawListSharedData (so you can use ImDrawList without ImGui) - ImDrawList(const ImDrawListSharedData* shared_data) { memset(this, 0, sizeof(*this)); _Data = shared_data; } + ImDrawList(ImDrawListSharedData* shared_data) { memset(this, 0, sizeof(*this)); _Data = shared_data; } ~ImDrawList() { _ClearFreeMemory(); } IMGUI_API void PushClipRect(const ImVec2& clip_rect_min, const ImVec2& clip_rect_max, bool intersect_with_current_clip_rect = false); // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling) @@ -2541,7 +2534,7 @@ struct ImDrawList // - For circle primitives, use "num_segments == 0" to automatically calculate tessellation (preferred). // In older versions (until Dear ImGui 1.77) the AddCircle functions defaulted to num_segments == 12. // In future versions we will use textures to provide cheaper and higher-quality circles. - // Use AddNgon() and AddNgonFilled() functions if you need to guaranteed a specific number of sides. + // Use AddNgon() and AddNgonFilled() functions if you need to guarantee a specific number of sides. IMGUI_API void AddLine(const ImVec2& p1, const ImVec2& p2, ImU32 col, float thickness = 1.0f); IMGUI_API void AddRect(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0, float thickness = 1.0f); // a: upper-left, b: lower-right (== upper-left + size) IMGUI_API void AddRectFilled(const ImVec2& p_min, const ImVec2& p_max, ImU32 col, float rounding = 0.0f, ImDrawFlags flags = 0); // a: upper-left, b: lower-right (== upper-left + size) @@ -2726,7 +2719,7 @@ enum ImFontAtlasFlags_ ImFontAtlasFlags_None = 0, ImFontAtlasFlags_NoPowerOfTwoHeight = 1 << 0, // Don't round the height to next power of two ImFontAtlasFlags_NoMouseCursors = 1 << 1, // Don't build software mouse cursors into the atlas (save a little texture memory) - ImFontAtlasFlags_NoBakedLines = 1 << 2 // Don't build thick line textures into the atlas (save a little texture memory, allow support for point/nearest filtering). The AntiAliasedLinesUseTex features uses them, otherwise they will be rendered using polygons (more expensive for CPU/GPU). + ImFontAtlasFlags_NoBakedLines = 1 << 2, // Don't build thick line textures into the atlas (save a little texture memory, allow support for point/nearest filtering). The AntiAliasedLinesUseTex features uses them, otherwise they will be rendered using polygons (more expensive for CPU/GPU). }; // Load and rasterize multiple TTF/OTF fonts into a same texture. The font atlas will build a single texture holding: @@ -2745,7 +2738,7 @@ enum ImFontAtlasFlags_ // - Important: By default, AddFontFromMemoryTTF() takes ownership of the data. Even though we are not writing to it, we will free the pointer on destruction. // You can set font_cfg->FontDataOwnedByAtlas=false to keep ownership of your data and it won't be freed, // - Even though many functions are suffixed with "TTF", OTF data is supported just as well. -// - This is an old API and it is currently awkward for those and and various other reasons! We will address them in the future! +// - This is an old API and it is currently awkward for those and various other reasons! We will address them in the future! struct ImFontAtlas { IMGUI_API ImFontAtlas(); @@ -2769,7 +2762,7 @@ struct ImFontAtlas IMGUI_API bool Build(); // Build pixels data. This is called automatically for you by the GetTexData*** functions. IMGUI_API void GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 1 byte per-pixel IMGUI_API void GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel = NULL); // 4 bytes-per-pixel - bool IsBuilt() const { return Fonts.Size > 0 && TexReady; } // Bit ambiguous: used to detect when user didn't built texture but effectively we should check TexID != 0 except that would be backend dependent... + bool IsBuilt() const { return Fonts.Size > 0 && TexReady; } // Bit ambiguous: used to detect when user didn't build texture but effectively we should check TexID != 0 except that would be backend dependent... void SetTexID(ImTextureID id) { TexID = id; } //------------------------------------------- @@ -2780,6 +2773,7 @@ struct ImFontAtlas // NB: Make sure that your string are UTF-8 and NOT in your local code page. In C++11, you can create UTF-8 string literal using the u8"Hello world" syntax. See FAQ for details. // NB: Consider using ImFontGlyphRangesBuilder to build glyph ranges from textual data. IMGUI_API const ImWchar* GetGlyphRangesDefault(); // Basic Latin, Extended Latin + IMGUI_API const ImWchar* GetGlyphRangesGreek(); // Default + Greek and Coptic IMGUI_API const ImWchar* GetGlyphRangesKorean(); // Default + Korean characters IMGUI_API const ImWchar* GetGlyphRangesJapanese(); // Default + Hiragana, Katakana, Half-Width, Selection of 2999 Ideographs IMGUI_API const ImWchar* GetGlyphRangesChineseFull(); // Default + Half-Width + Japanese Hiragana/Katakana + full set of about 21000 CJK Unified Ideographs @@ -2816,6 +2810,7 @@ struct ImFontAtlas int TexDesiredWidth; // Texture width desired by user before Build(). Must be a power-of-two. If have many glyphs your graphics API have texture size restrictions you may want to increase texture width to decrease height. int TexGlyphPadding; // Padding between glyphs within texture in pixels. Defaults to 1. If your rendering method doesn't rely on bilinear filtering you may set this to 0 (will also need to set AntiAliasedLinesUseTex = false). bool Locked; // Marked as Locked by ImGui::NewFrame() so attempt to modify the atlas will assert. + void* UserData; // Store your own atlas related user-data (if e.g. you have multiple font atlas). // [Internal] // NB: Access texture data via GetTexData*() calls! Which will setup a default font for you. @@ -2908,7 +2903,7 @@ enum ImGuiViewportFlags_ ImGuiViewportFlags_None = 0, ImGuiViewportFlags_IsPlatformWindow = 1 << 0, // Represent a Platform Window ImGuiViewportFlags_IsPlatformMonitor = 1 << 1, // Represent a Platform Monitor (unused yet) - ImGuiViewportFlags_OwnedByApp = 1 << 2 // Platform Window: is created/managed by the application (rather than a dear imgui backend) + ImGuiViewportFlags_OwnedByApp = 1 << 2, // Platform Window: is created/managed by the application (rather than a dear imgui backend) }; // - Currently represents the Platform Window created by the application which is hosting our Dear ImGui windows. @@ -2959,53 +2954,54 @@ struct ImGuiPlatformImeData namespace ImGui { #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - IMGUI_API int GetKeyIndex(ImGuiKey key); // map ImGuiKey_* values into legacy native key index. == io.KeyMap[key] + IMGUI_API ImGuiKey GetKeyIndex(ImGuiKey key); // map ImGuiKey_* values into legacy native key index. == io.KeyMap[key] #else - static inline int GetKeyIndex(ImGuiKey key) { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END && "ImGuiKey and native_index was merged together and native_index is disabled by IMGUI_DISABLE_OBSOLETE_KEYIO. Please switch to ImGuiKey."); return key; } + static inline ImGuiKey GetKeyIndex(ImGuiKey key) { IM_ASSERT(key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END && "ImGuiKey and native_index was merged together and native_index is disabled by IMGUI_DISABLE_OBSOLETE_KEYIO. Please switch to ImGuiKey."); return key; } #endif } #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS namespace ImGui { + // OBSOLETED in 1.89 (from August 2022) + IMGUI_API bool ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), int frame_padding = -1, const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); // Use new ImageButton() signature (explicit item id, regular FramePadding) // OBSOLETED in 1.88 (from May 2022) - static inline void CaptureKeyboardFromApp(bool want_capture_keyboard = true) { SetNextFrameWantCaptureKeyboard(want_capture_keyboard); } // Renamed as name was misleading + removed default value. - static inline void CaptureMouseFromApp(bool want_capture_mouse = true) { SetNextFrameWantCaptureMouse(want_capture_mouse); } // Renamed as name was misleading + removed default value. + static inline void CaptureKeyboardFromApp(bool want_capture_keyboard = true) { SetNextFrameWantCaptureKeyboard(want_capture_keyboard); } // Renamed as name was misleading + removed default value. + static inline void CaptureMouseFromApp(bool want_capture_mouse = true) { SetNextFrameWantCaptureMouse(want_capture_mouse); } // Renamed as name was misleading + removed default value. // OBSOLETED in 1.86 (from November 2021) IMGUI_API void CalcListClipping(int items_count, float items_height, int* out_items_display_start, int* out_items_display_end); // Calculate coarse clipping for large list of evenly sized items. Prefer using ImGuiListClipper. // OBSOLETED in 1.85 (from August 2021) - static inline float GetWindowContentRegionWidth() { return GetWindowContentRegionMax().x - GetWindowContentRegionMin().x; } + static inline float GetWindowContentRegionWidth() { return GetWindowContentRegionMax().x - GetWindowContentRegionMin().x; } // OBSOLETED in 1.81 (from February 2021) IMGUI_API bool ListBoxHeader(const char* label, int items_count, int height_in_items = -1); // Helper to calculate size from items_count and height_in_items - static inline bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0, 0)) { return BeginListBox(label, size); } - static inline void ListBoxFooter() { EndListBox(); } - // OBSOLETED in 1.79 (from August 2020) - static inline void OpenPopupContextItem(const char* str_id = NULL, ImGuiMouseButton mb = 1) { OpenPopupOnItemClick(str_id, mb); } // Bool return value removed. Use IsWindowAppearing() in BeginPopup() instead. Renamed in 1.77, renamed back in 1.79. Sorry! - // OBSOLETED in 1.78 (from June 2020) - // Old drag/sliders functions that took a 'float power = 1.0' argument instead of flags. - // For shared code, you can version check at compile-time with `#if IMGUI_VERSION_NUM >= 17704`. - IMGUI_API bool DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, const char* format, float power); - IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min, const void* p_max, const char* format, float power); - static inline bool DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, float power) { return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, power); } - static inline bool DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, float power) { return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, power); } - static inline bool DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, float power) { return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, power); } - static inline bool DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, float power) { return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, power); } - IMGUI_API bool SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, float power); - IMGUI_API bool SliderScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_min, const void* p_max, const char* format, float power); - static inline bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, float power) { return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, power); } - static inline bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, float power) { return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, power); } - static inline bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, float power) { return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, power); } - static inline bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, float power) { return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, power); } - // OBSOLETED in 1.77 (from June 2020) - static inline bool BeginPopupContextWindow(const char* str_id, ImGuiMouseButton mb, bool over_items) { return BeginPopupContextWindow(str_id, mb | (over_items ? 0 : ImGuiPopupFlags_NoOpenOverItems)); } + static inline bool ListBoxHeader(const char* label, const ImVec2& size = ImVec2(0, 0)) { return BeginListBox(label, size); } + static inline void ListBoxFooter() { EndListBox(); } // Some of the older obsolete names along with their replacement (commented out so they are not reported in IDE) + //-- OBSOLETED in 1.79 (from August 2020) + //static inline void OpenPopupContextItem(const char* str_id = NULL, ImGuiMouseButton mb = 1) { OpenPopupOnItemClick(str_id, mb); } // Bool return value removed. Use IsWindowAppearing() in BeginPopup() instead. Renamed in 1.77, renamed back in 1.79. Sorry! + //-- OBSOLETED in 1.78 (from June 2020): Old drag/sliders functions that took a 'float power > 1.0f' argument instead of ImGuiSliderFlags_Logarithmic. See github.com/ocornut/imgui/issues/3361 for details. + //IMGUI_API bool DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, const char* format, float power = 1.0f) // OBSOLETED in 1.78 (from June 2020) + //IMGUI_API bool DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min, const void* p_max, const char* format, float power = 1.0f); // OBSOLETED in 1.78 (from June 2020) + //IMGUI_API bool SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, float power = 1.0f); // OBSOLETED in 1.78 (from June 2020) + //IMGUI_API bool SliderScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, const void* p_min, const void* p_max, const char* format, float power = 1.0f); // OBSOLETED in 1.78 (from June 2020) + //static inline bool DragFloat(const char* label, float* v, float v_speed, float v_min, float v_max, const char* format, float power = 1.0f) { return DragScalar(label, ImGuiDataType_Float, v, v_speed, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020) + //static inline bool DragFloat2(const char* label, float v[2], float v_speed, float v_min, float v_max, const char* format, float power = 1.0f) { return DragScalarN(label, ImGuiDataType_Float, v, 2, v_speed, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020) + //static inline bool DragFloat3(const char* label, float v[3], float v_speed, float v_min, float v_max, const char* format, float power = 1.0f) { return DragScalarN(label, ImGuiDataType_Float, v, 3, v_speed, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020) + //static inline bool DragFloat4(const char* label, float v[4], float v_speed, float v_min, float v_max, const char* format, float power = 1.0f) { return DragScalarN(label, ImGuiDataType_Float, v, 4, v_speed, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020) + //static inline bool SliderFloat(const char* label, float* v, float v_min, float v_max, const char* format, float power = 1.0f) { return SliderScalar(label, ImGuiDataType_Float, v, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020) + //static inline bool SliderFloat2(const char* label, float v[2], float v_min, float v_max, const char* format, float power = 1.0f) { return SliderScalarN(label, ImGuiDataType_Float, v, 2, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020) + //static inline bool SliderFloat3(const char* label, float v[3], float v_min, float v_max, const char* format, float power = 1.0f) { return SliderScalarN(label, ImGuiDataType_Float, v, 3, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020) + //static inline bool SliderFloat4(const char* label, float v[4], float v_min, float v_max, const char* format, float power = 1.0f) { return SliderScalarN(label, ImGuiDataType_Float, v, 4, &v_min, &v_max, format, power); } // OBSOLETED in 1.78 (from June 2020) + //-- OBSOLETED in 1.77 and before + //static inline bool BeginPopupContextWindow(const char* str_id, ImGuiMouseButton mb, bool over_items) { return BeginPopupContextWindow(str_id, mb | (over_items ? 0 : ImGuiPopupFlags_NoOpenOverItems)); } // OBSOLETED in 1.77 (from June 2020) //static inline void TreeAdvanceToLabelPos() { SetCursorPosX(GetCursorPosX() + GetTreeNodeToLabelSpacing()); } // OBSOLETED in 1.72 (from July 2019) //static inline void SetNextTreeNodeOpen(bool open, ImGuiCond cond = 0) { SetNextItemOpen(open, cond); } // OBSOLETED in 1.71 (from June 2019) //static inline float GetContentRegionAvailWidth() { return GetContentRegionAvail().x; } // OBSOLETED in 1.70 (from May 2019) //static inline ImDrawList* GetOverlayDrawList() { return GetForegroundDrawList(); } // OBSOLETED in 1.69 (from Mar 2019) //static inline void SetScrollHere(float ratio = 0.5f) { SetScrollHereY(ratio); } // OBSOLETED in 1.66 (from Nov 2018) //static inline bool IsItemDeactivatedAfterChange() { return IsItemDeactivatedAfterEdit(); } // OBSOLETED in 1.63 (from Aug 2018) + //-- OBSOLETED in 1.60 and before //static inline bool IsAnyWindowFocused() { return IsWindowFocused(ImGuiFocusedFlags_AnyWindow); } // OBSOLETED in 1.60 (from Apr 2018) //static inline bool IsAnyWindowHovered() { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); } // OBSOLETED in 1.60 (between Dec 2017 and Apr 2018) //static inline void ShowTestWindow() { return ShowDemoWindow(); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) @@ -3013,6 +3009,19 @@ namespace ImGui //static inline bool IsRootWindowOrAnyChildFocused() { return IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) //static inline void SetNextWindowContentWidth(float w) { SetNextWindowContentSize(ImVec2(w, 0.0f)); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) //static inline float GetItemsLineHeightWithSpacing() { return GetFrameHeightWithSpacing(); } // OBSOLETED in 1.53 (between Oct 2017 and Dec 2017) + //IMGUI_API bool Begin(char* name, bool* p_open, ImVec2 size_first_use, float bg_alpha = -1.0f, ImGuiWindowFlags flags=0); // OBSOLETED in 1.52 (between Aug 2017 and Oct 2017): Equivalent of using SetNextWindowSize(size, ImGuiCond_FirstUseEver) and SetNextWindowBgAlpha(). + //static inline bool IsRootWindowOrAnyChildHovered() { return IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows); } // OBSOLETED in 1.52 (between Aug 2017 and Oct 2017) + //static inline void AlignFirstTextHeightToWidgets() { AlignTextToFramePadding(); } // OBSOLETED in 1.52 (between Aug 2017 and Oct 2017) + //static inline void SetNextWindowPosCenter(ImGuiCond c=0) { SetNextWindowPos(GetMainViewport()->GetCenter(), c, ImVec2(0.5f,0.5f)); } // OBSOLETED in 1.52 (between Aug 2017 and Oct 2017) + //static inline bool IsItemHoveredRect() { return IsItemHovered(ImGuiHoveredFlags_RectOnly); } // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017) + //static inline bool IsPosHoveringAnyWindow(const ImVec2&) { IM_ASSERT(0); return false; } // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017): This was misleading and partly broken. You probably want to use the io.WantCaptureMouse flag instead. + //static inline bool IsMouseHoveringAnyWindow() { return IsWindowHovered(ImGuiHoveredFlags_AnyWindow); } // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017) + //static inline bool IsMouseHoveringWindow() { return IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem); } // OBSOLETED in 1.51 (between Jun 2017 and Aug 2017) + //-- OBSOLETED in 1.50 and before + //static inline bool CollapsingHeader(char* label, const char* str_id, bool framed = true, bool default_open = false) { return CollapsingHeader(label, (default_open ? (1 << 5) : 0)); } // OBSOLETED in 1.49 + //static inline ImFont*GetWindowFont() { return GetFont(); } // OBSOLETED in 1.48 + //static inline float GetWindowFontSize() { return GetFontSize(); } // OBSOLETED in 1.48 + //static inline void SetScrollPosHere() { SetScrollHere(); } // OBSOLETED in 1.42 } // OBSOLETED in 1.82 (from Mars 2021): flags for AddRect(), AddRectFilled(), AddImageRounded(), PathRect() @@ -3028,12 +3037,15 @@ enum ImDrawCornerFlags_ ImDrawCornerFlags_Top = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_TopRight, ImDrawCornerFlags_Bot = ImDrawCornerFlags_BotLeft | ImDrawCornerFlags_BotRight, ImDrawCornerFlags_Left = ImDrawCornerFlags_TopLeft | ImDrawCornerFlags_BotLeft, - ImDrawCornerFlags_Right = ImDrawCornerFlags_TopRight | ImDrawCornerFlags_BotRight + ImDrawCornerFlags_Right = ImDrawCornerFlags_TopRight | ImDrawCornerFlags_BotRight, }; -// RENAMED ImGuiKeyModFlags -> ImGuiModFlags in 1.88 (from April 2022) -typedef int ImGuiKeyModFlags; -enum ImGuiKeyModFlags_ { ImGuiKeyModFlags_None = ImGuiModFlags_None, ImGuiKeyModFlags_Ctrl = ImGuiModFlags_Ctrl, ImGuiKeyModFlags_Shift = ImGuiModFlags_Shift, ImGuiKeyModFlags_Alt = ImGuiModFlags_Alt, ImGuiKeyModFlags_Super = ImGuiModFlags_Super }; +// RENAMED and MERGED both ImGuiKey_ModXXX and ImGuiModFlags_XXX into ImGuiMod_XXX (from September 2022) +// RENAMED ImGuiKeyModFlags -> ImGuiModFlags in 1.88 (from April 2022). Exceptionally commented out ahead of obscolescence schedule to reduce confusion and because they were not meant to be used in the first place. +typedef ImGuiKeyChord ImGuiModFlags; // == int. We generally use ImGuiKeyChord to mean "a ImGuiKey or-ed with any number of ImGuiMod_XXX value", but you may store only mods in there. +enum ImGuiModFlags_ { ImGuiModFlags_None = 0, ImGuiModFlags_Ctrl = ImGuiMod_Ctrl, ImGuiModFlags_Shift = ImGuiMod_Shift, ImGuiModFlags_Alt = ImGuiMod_Alt, ImGuiModFlags_Super = ImGuiMod_Super }; +//typedef ImGuiKeyChord ImGuiKeyModFlags; // == int +//enum ImGuiKeyModFlags_ { ImGuiKeyModFlags_None = 0, ImGuiKeyModFlags_Ctrl = ImGuiMod_Ctrl, ImGuiKeyModFlags_Shift = ImGuiMod_Shift, ImGuiKeyModFlags_Alt = ImGuiMod_Alt, ImGuiKeyModFlags_Super = ImGuiMod_Super }; #endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS diff --git a/extern/src/imgui/imgui_draw.cpp b/extern/src/imgui/imgui_draw.cpp index fcdb9dfe..2d423cd8 100644 --- a/extern/src/imgui/imgui_draw.cpp +++ b/extern/src/imgui/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.88 +// dear imgui, v1.89.2 // (drawing and font code) /* @@ -39,25 +39,12 @@ Index of this file: #endif #include // vsnprintf, sscanf, printf -#if !defined(alloca) -#if defined(__GLIBC__) || defined(__sun) || defined(__APPLE__) || defined(__NEWLIB__) -#include // alloca (glibc uses . Note that Cygwin may have _WIN32 defined, so the order matters here) -#elif defined(_WIN32) -#include // alloca -#if !defined(alloca) -#define alloca _alloca // for clang with MS Codegen -#endif -#else -#include // alloca -#endif -#endif // Visual Studio warnings #ifdef _MSC_VER #pragma warning (disable: 4127) // condition expression is constant #pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff) #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen -#pragma warning (disable: 6255) // [Static Analyzer] _alloca indicates failure by raising a stack overflow exception. Consider using _malloca instead. #pragma warning (disable: 26451) // [Static Analyzer] Arithmetic overflow : Using operator 'xxx' on a 4 byte value and then casting the result to a 8 byte value. Cast the value to the wider type before calling operator 'xxx' to avoid overflow(io.2). #pragma warning (disable: 26812) // [Static Analyzer] The enum type 'xxx' is unscoped. Prefer 'enum class' over 'enum' (Enum.3). [MSVC Static Analyzer) #endif @@ -67,9 +54,6 @@ Index of this file: #if __has_warning("-Wunknown-warning-option") #pragma clang diagnostic ignored "-Wunknown-warning-option" // warning: unknown warning group 'xxx' // not all warnings are known by all Clang versions and they tend to be rename-happy.. so ignoring warnings triggers new warnings on some configuration. Great! #endif -#if __has_warning("-Walloca") -#pragma clang diagnostic ignored "-Walloca" // warning: use of function '__builtin_alloca' is discouraged -#endif #pragma clang diagnostic ignored "-Wunknown-pragmas" // warning: unknown warning group 'xxx' #pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast // yes, they are more terse. #pragma clang diagnostic ignored "-Wfloat-equal" // warning: comparing floating point with == or != is unsafe // storing and comparing against same constants ok. @@ -463,11 +447,13 @@ void ImDrawList::AddDrawCmd() // Note that this leaves the ImDrawList in a state unfit for further commands, as most code assume that CmdBuffer.Size > 0 && CmdBuffer.back().UserCallback == NULL void ImDrawList::_PopUnusedDrawCmd() { - if (CmdBuffer.Size == 0) - return; - ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; - if (curr_cmd->ElemCount == 0 && curr_cmd->UserCallback == NULL) + while (CmdBuffer.Size > 0) + { + ImDrawCmd* curr_cmd = &CmdBuffer.Data[CmdBuffer.Size - 1]; + if (curr_cmd->ElemCount != 0 || curr_cmd->UserCallback != NULL) + return;// break; CmdBuffer.pop_back(); + } } void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data) @@ -753,7 +739,8 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 // Temporary buffer // The first items are normals at each line point, then after that there are either 2 or 4 temp points for each line point - ImVec2* temp_normals = (ImVec2*)alloca(points_count * ((use_texture || !thick_line) ? 3 : 5) * sizeof(ImVec2)); //-V630 + _Data->TempBuffer.reserve_discard(points_count * ((use_texture || !thick_line) ? 3 : 5)); + ImVec2* temp_normals = _Data->TempBuffer.Data; ImVec2* temp_points = temp_normals + points_count; // Calculate normals (tangents) for each line segment @@ -1001,7 +988,8 @@ void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_coun } // Compute normals - ImVec2* temp_normals = (ImVec2*)alloca(points_count * sizeof(ImVec2)); //-V630 + _Data->TempBuffer.reserve_discard(points_count); + ImVec2* temp_normals = _Data->TempBuffer.Data; for (int i0 = points_count - 1, i1 = 0; i1 < points_count; i0 = i1++) { const ImVec2& p0 = points[i0]; @@ -1295,6 +1283,7 @@ void ImDrawList::PathBezierCubicCurveTo(const ImVec2& p2, const ImVec2& p3, cons ImVec2 p1 = _Path.back(); if (num_segments == 0) { + IM_ASSERT(_Data->CurveTessellationTol > 0.0f); PathBezierCubicCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, _Data->CurveTessellationTol, 0); // Auto-tessellated } else @@ -1310,6 +1299,7 @@ void ImDrawList::PathBezierQuadraticCurveTo(const ImVec2& p2, const ImVec2& p3, ImVec2 p1 = _Path.back(); if (num_segments == 0) { + IM_ASSERT(_Data->CurveTessellationTol > 0.0f); PathBezierQuadraticCurveToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, _Data->CurveTessellationTol, 0);// Auto-tessellated } else @@ -1324,6 +1314,7 @@ IM_STATIC_ASSERT(ImDrawFlags_RoundCornersTopLeft == (1 << 4)); static inline ImDrawFlags FixRectCornerFlags(ImDrawFlags flags) { #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + // Obsoleted in 1.82 (from February 2021) // Legacy Support for hard coded ~0 (used to be a suggested equivalent to ImDrawCornerFlags_All) // ~0 --> ImDrawFlags_RoundCornersAll or 0 if (flags == ~0) @@ -2298,10 +2289,11 @@ void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], fl void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride) { + IM_ASSERT_PARANOID(w <= stride); unsigned char* data = pixels + x + y * stride; - for (int j = h; j > 0; j--, data += stride) - for (int i = 0; i < w; i++) - data[i] = table[data[i]]; + for (int j = h; j > 0; j--, data += stride - w) + for (int i = w; i > 0; i--, data++) + *data = table[*data]; } #ifdef IMGUI_ENABLE_STB_TRUETYPE @@ -2318,7 +2310,7 @@ struct ImFontBuildSrcData int GlyphsHighest; // Highest requested codepoint int GlyphsCount; // Glyph count (excluding missing glyphs and glyphs already set by an earlier source font) ImBitVector GlyphsSet; // Glyph bit map (random access, 1-bit per codepoint. This will be a maximum of 8KB) - ImVector GlyphsList; // Glyph codepoints list (flattened version of GlyphsMap) + ImVector GlyphsList; // Glyph codepoints list (flattened version of GlyphsSet) }; // Temporary data for one destination ImFont* (multiple source fonts can be merged into one destination ImFont) @@ -2818,6 +2810,17 @@ const ImWchar* ImFontAtlas::GetGlyphRangesDefault() return &ranges[0]; } +const ImWchar* ImFontAtlas::GetGlyphRangesGreek() +{ + static const ImWchar ranges[] = + { + 0x0020, 0x00FF, // Basic Latin + Latin Supplement + 0x0370, 0x03FF, // Greek and Coptic + 0, + }; + return &ranges[0]; +} + const ImWchar* ImFontAtlas::GetGlyphRangesKorean() { static const ImWchar ranges[] = @@ -3330,11 +3333,21 @@ const ImFontGlyph* ImFont::FindGlyphNoFallback(ImWchar c) const return &Glyphs.Data[i]; } +// Wrapping skips upcoming blanks +static inline const char* CalcWordWrapNextLineStartA(const char* text, const char* text_end) +{ + while (text < text_end && ImCharIsBlankA(*text)) + text++; + if (*text == '\n') + text++; + return text; +} + +// Simple word-wrapping for English, not full-featured. Please submit failing cases! +// This will return the next location to wrap from. If no wrapping if necessary, this will fast-forward to e.g. text_end. +// FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.) const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const char* text_end, float wrap_width) const { - // Simple word-wrapping for English, not full-featured. Please submit failing cases! - // FIXME: Much possible improvements (don't cut things like "word !", "word!!!" but cut within "word,,,,", more sensible support for punctuations, support for Unicode punctuations, etc.) - // For references, possible wrap point marked with ^ // "aaa bbb, ccc,ddd. eee fff. ggg!" // ^ ^ ^ ^ ^__ ^ ^ @@ -3346,7 +3359,6 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c // Cut words that cannot possibly fit within one line. // e.g.: "The tropical fish" with ~5 characters worth of width --> "The tr" "opical" "fish" - float line_width = 0.0f; float word_width = 0.0f; float blank_width = 0.0f; @@ -3426,6 +3438,10 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c s = next_s; } + // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity. + // +1 may not be a character start point in UTF-8 but it's ok because caller loops use (text >= word_wrap_eol). + if (s == text && text < text_end) + return s + 1; return s; } @@ -3450,11 +3466,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons { // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature. if (!word_wrap_eol) - { word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - line_width); - if (word_wrap_eol == s) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity. - word_wrap_eol++; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below - } if (s >= word_wrap_eol) { @@ -3463,13 +3475,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons text_size.y += line_height; line_width = 0.0f; word_wrap_eol = NULL; - - // Wrapping skips upcoming blanks - while (s < text_end) - { - const char c = *s; - if (ImCharIsBlankA(c)) { s++; } else if (c == '\n') { s++; break; } else { break; } - } + s = CalcWordWrapNextLineStartA(s, text_end); // Wrapping skips upcoming blanks continue; } } @@ -3554,15 +3560,26 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im const float scale = size / FontSize; const float line_height = FontSize * scale; const bool word_wrap_enabled = (wrap_width > 0.0f); - const char* word_wrap_eol = NULL; // Fast-forward to first visible line const char* s = text_begin; - if (y + line_height < clip_rect.y && !word_wrap_enabled) + if (y + line_height < clip_rect.y) while (y + line_height < clip_rect.y && s < text_end) { - s = (const char*)memchr(s, '\n', text_end - s); - s = s ? s + 1 : text_end; + const char* line_end = (const char*)memchr(s, '\n', text_end - s); + const char* line_next = line_end ? line_end + 1 : text_end; + if (word_wrap_enabled) + { + // FIXME-OPT: This is not optimal as do first do a search for \n before calling CalcWordWrapPositionA(). + // If the specs for CalcWordWrapPositionA() were reworked to optionally return on \n we could combine both. + // However it is still better than nothing performing the fast-forward! + s = CalcWordWrapPositionA(scale, s, line_next, wrap_width); + s = CalcWordWrapNextLineStartA(s, text_end); + } + else + { + s = line_next; + } y += line_height; } @@ -3594,6 +3611,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im unsigned int vtx_current_idx = draw_list->_VtxCurrentIdx; const ImU32 col_untinted = col | ~IM_COL32_A_MASK; + const char* word_wrap_eol = NULL; while (s < text_end) { @@ -3601,24 +3619,14 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, const ImVec2& pos, Im { // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and not intrusive for what's essentially an uncommon feature. if (!word_wrap_eol) - { word_wrap_eol = CalcWordWrapPositionA(scale, s, text_end, wrap_width - (x - start_x)); - if (word_wrap_eol == s) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity. - word_wrap_eol++; // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below - } if (s >= word_wrap_eol) { x = start_x; y += line_height; word_wrap_eol = NULL; - - // Wrapping skips upcoming blanks - while (s < text_end) - { - const char c = *s; - if (ImCharIsBlankA(c)) { s++; } else if (c == '\n') { s++; break; } else { break; } - } + s = CalcWordWrapNextLineStartA(s, text_end); // Wrapping skips upcoming blanks continue; } } diff --git a/extern/src/imgui/imgui_impl_opengl3.cpp b/extern/src/imgui/imgui_impl_opengl3.cpp index e52e0b53..c98110d8 100644 --- a/extern/src/imgui/imgui_impl_opengl3.cpp +++ b/extern/src/imgui/imgui_impl_opengl3.cpp @@ -14,6 +14,9 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2022-11-09: OpenGL: Reverted use of glBufferSubData(), too many corruptions issues + old issues seemingly can't be reproed with Intel drivers nowadays (revert 2021-12-15 and 2022-05-23 changes). +// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. +// 2022-09-27: OpenGL: Added ability to '#define IMGUI_IMPL_OPENGL_DEBUG'. // 2022-05-23: OpenGL: Reworking 2021-12-15 "Using buffer orphaning" so it only happens on Intel GPU, seems to cause problems otherwise. (#4468, #4825, #4832, #5127). // 2022-05-13: OpenGL: Fix state corruption on OpenGL ES 2.0 due to not preserving GL_ELEMENT_ARRAY_BUFFER_BINDING and vertex attribute states. // 2021-12-15: OpenGL: Using buffer orphaning + glBufferSubData(), seems to fix leaks with multi-viewports with some Intel HD drivers. @@ -56,7 +59,7 @@ // 2018-06-08: Misc: Extracted imgui_impl_opengl3.cpp/.h away from the old combined GLFW/SDL+OpenGL3 examples. // 2018-06-08: OpenGL: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. // 2018-05-25: OpenGL: Removed unnecessary backup/restore of GL_ELEMENT_ARRAY_BUFFER_BINDING since this is part of the VAO state. -// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a NULL pointer. +// 2018-05-14: OpenGL: Making the call to glBindSampler() optional so 3.2 context won't fail if the function is a nullptr pointer. // 2018-03-06: OpenGL: Added const char* glsl_version parameter to ImGui_ImplOpenGL3_Init() so user can override the GLSL version e.g. "#version 150". // 2018-02-23: OpenGL: Create the VAO in the render function so the setup can more easily be used with multiple shared GL context. // 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback and exposed ImGui_ImplSdlGL3_RenderDrawData() in the .h file so you can call it yourself. @@ -106,9 +109,10 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wold-style-cast" // warning: use of old-style cast #pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness -#if __has_warning("-Wzero-as-null-pointer-constant") -#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" #endif +#if defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type" // warning: cast between incompatible function types #endif // GL includes @@ -178,6 +182,15 @@ #define IMGUI_IMPL_OPENGL_MAY_HAVE_EXTENSIONS #endif +// [Debugging] +//#define IMGUI_IMPL_OPENGL_DEBUG +#ifdef IMGUI_IMPL_OPENGL_DEBUG +#include +#define GL_CALL(_CALL) do { _CALL; GLenum gl_err = glGetError(); if (gl_err != 0) fprintf(stderr, "GL error 0x%x returned from '%s'.\n", gl_err, #_CALL); } while (0) // Call with error check +#else +#define GL_CALL(_CALL) _CALL // Call without error check +#endif + // OpenGL Data struct ImGui_ImplOpenGL3_Data { @@ -203,7 +216,7 @@ struct ImGui_ImplOpenGL3_Data // It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. static ImGui_ImplOpenGL3_Data* ImGui_ImplOpenGL3_GetBackendData() { - return ImGui::GetCurrentContext() ? (ImGui_ImplOpenGL3_Data*)ImGui::GetIO().BackendRendererUserData : NULL; + return ImGui::GetCurrentContext() ? (ImGui_ImplOpenGL3_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; } // OpenGL vertex attribute state (for ES 1.0 and ES 2.0 only) @@ -234,7 +247,7 @@ struct ImGui_ImplOpenGL3_VtxAttribState bool ImGui_ImplOpenGL3_Init(const char* glsl_version) { ImGuiIO& io = ImGui::GetIO(); - IM_ASSERT(io.BackendRendererUserData == NULL && "Already initialized a renderer backend!"); + IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); // Initialize our loader #if !defined(IMGUI_IMPL_OPENGL_ES2) && !defined(IMGUI_IMPL_OPENGL_ES3) && !defined(IMGUI_IMPL_OPENGL_LOADER_CUSTOM) @@ -264,25 +277,31 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) } bd->GlVersion = (GLuint)(major * 100 + minor * 10); + bd->UseBufferSubData = false; + /* // Query vendor to enable glBufferSubData kludge #ifdef _WIN32 if (const char* vendor = (const char*)glGetString(GL_VENDOR)) if (strncmp(vendor, "Intel", 5) == 0) bd->UseBufferSubData = true; #endif - //printf("GL_MAJOR_VERSION = %d\nGL_MINOR_VERSION = %d\nGL_VENDOR = '%s'\nGL_RENDERER = '%s'\n", major, minor, (const char*)glGetString(GL_VENDOR), (const char*)glGetString(GL_RENDERER)); // [DEBUG] + */ #else bd->GlVersion = 200; // GLES 2 #endif +#ifdef IMGUI_IMPL_OPENGL_DEBUG + printf("GL_MAJOR_VERSION = %d\nGL_MINOR_VERSION = %d\nGL_VENDOR = '%s'\nGL_RENDERER = '%s'\n", major, minor, (const char*)glGetString(GL_VENDOR), (const char*)glGetString(GL_RENDERER)); // [DEBUG] +#endif + #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET if (bd->GlVersion >= 320) io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. #endif // Store GLSL version string so we can refer to it later in case we recreate shaders. - // Note: GLSL version is NOT the same as GL version. Leave this to NULL if unsure. - if (glsl_version == NULL) + // Note: GLSL version is NOT the same as GL version. Leave this to nullptr if unsure. + if (glsl_version == nullptr) { #if defined(IMGUI_IMPL_OPENGL_ES2) glsl_version = "#version 100"; @@ -311,7 +330,7 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) for (GLint i = 0; i < num_extensions; i++) { const char* extension = (const char*)glGetStringi(GL_EXTENSIONS, i); - if (extension != NULL && strcmp(extension, "GL_ARB_clip_control") == 0) + if (extension != nullptr && strcmp(extension, "GL_ARB_clip_control") == 0) bd->HasClipOrigin = true; } #endif @@ -322,19 +341,19 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) void ImGui_ImplOpenGL3_Shutdown() { ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); - IM_ASSERT(bd != NULL && "No renderer backend to shutdown, or already shutdown?"); + IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); ImGuiIO& io = ImGui::GetIO(); ImGui_ImplOpenGL3_DestroyDeviceObjects(); - io.BackendRendererName = NULL; - io.BackendRendererUserData = NULL; + io.BackendRendererName = nullptr; + io.BackendRendererUserData = nullptr; IM_DELETE(bd); } void ImGui_ImplOpenGL3_NewFrame() { ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData(); - IM_ASSERT(bd != NULL && "Did you call ImGui_ImplOpenGL3_Init()?"); + IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplOpenGL3_Init()?"); if (!bd->ShaderHandle) ImGui_ImplOpenGL3_CreateDeviceObjects(); @@ -373,7 +392,7 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid // Setup viewport, orthographic projection matrix // Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. - glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height); + GL_CALL(glViewport(0, 0, (GLsizei)fb_width, (GLsizei)fb_height)); float L = draw_data->DisplayPos.x; float R = draw_data->DisplayPos.x + draw_data->DisplaySize.x; float T = draw_data->DisplayPos.y; @@ -403,14 +422,14 @@ static void ImGui_ImplOpenGL3_SetupRenderState(ImDrawData* draw_data, int fb_wid #endif // Bind vertex/index buffers and setup attributes for ImDrawVert - glBindBuffer(GL_ARRAY_BUFFER, bd->VboHandle); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bd->ElementsHandle); - glEnableVertexAttribArray(bd->AttribLocationVtxPos); - glEnableVertexAttribArray(bd->AttribLocationVtxUV); - glEnableVertexAttribArray(bd->AttribLocationVtxColor); - glVertexAttribPointer(bd->AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos)); - glVertexAttribPointer(bd->AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv)); - glVertexAttribPointer(bd->AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col)); + GL_CALL(glBindBuffer(GL_ARRAY_BUFFER, bd->VboHandle)); + GL_CALL(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bd->ElementsHandle)); + GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxPos)); + GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxUV)); + GL_CALL(glEnableVertexAttribArray(bd->AttribLocationVtxColor)); + GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxPos, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, pos))); + GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxUV, 2, GL_FLOAT, GL_FALSE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, uv))); + GL_CALL(glVertexAttribPointer(bd->AttribLocationVtxColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(ImDrawVert), (GLvoid*)IM_OFFSETOF(ImDrawVert, col))); } // OpenGL3 Render function. @@ -470,7 +489,7 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) // The renderer would actually work without any VAO bound, but then our VertexAttrib calls would overwrite the default one currently bound. GLuint vertex_array_object = 0; #ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY - glGenVertexArrays(1, &vertex_array_object); + GL_CALL(glGenVertexArrays(1, &vertex_array_object)); #endif ImGui_ImplOpenGL3_SetupRenderState(draw_data, fb_width, fb_height, vertex_array_object); @@ -484,9 +503,13 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) const ImDrawList* cmd_list = draw_data->CmdLists[n]; // Upload vertex/index buffers - // - On Intel windows drivers we got reports that regular glBufferData() led to accumulating leaks when using multi-viewports, so we started using orphaning + glBufferSubData(). (See https://github.com/ocornut/imgui/issues/4468) - // - On NVIDIA drivers we got reports that using orphaning + glBufferSubData() led to glitches when using multi-viewports. - // - OpenGL drivers are in a very sorry state in 2022, for now we are switching code path based on vendors. + // - OpenGL drivers are in a very sorry state nowadays.... + // During 2021 we attempted to switch from glBufferData() to orphaning+glBufferSubData() following reports + // of leaks on Intel GPU when using multi-viewports on Windows. + // - After this we kept hearing of various display corruptions issues. We started disabling on non-Intel GPU, but issues still got reported on Intel. + // - We are now back to using exclusively glBufferData(). So bd->UseBufferSubData IS ALWAYS FALSE in this code. + // We are keeping the old code path for a while in case people finding new issues may want to test the bd->UseBufferSubData path. + // - See https://github.com/ocornut/imgui/issues/4468 and please report any corruption issues. const GLsizeiptr vtx_buffer_size = (GLsizeiptr)cmd_list->VtxBuffer.Size * (int)sizeof(ImDrawVert); const GLsizeiptr idx_buffer_size = (GLsizeiptr)cmd_list->IdxBuffer.Size * (int)sizeof(ImDrawIdx); if (bd->UseBufferSubData) @@ -494,26 +517,26 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) if (bd->VertexBufferSize < vtx_buffer_size) { bd->VertexBufferSize = vtx_buffer_size; - glBufferData(GL_ARRAY_BUFFER, bd->VertexBufferSize, NULL, GL_STREAM_DRAW); + GL_CALL(glBufferData(GL_ARRAY_BUFFER, bd->VertexBufferSize, nullptr, GL_STREAM_DRAW)); } if (bd->IndexBufferSize < idx_buffer_size) { bd->IndexBufferSize = idx_buffer_size; - glBufferData(GL_ELEMENT_ARRAY_BUFFER, bd->IndexBufferSize, NULL, GL_STREAM_DRAW); + GL_CALL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, bd->IndexBufferSize, nullptr, GL_STREAM_DRAW)); } - glBufferSubData(GL_ARRAY_BUFFER, 0, vtx_buffer_size, (const GLvoid*)cmd_list->VtxBuffer.Data); - glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, idx_buffer_size, (const GLvoid*)cmd_list->IdxBuffer.Data); + GL_CALL(glBufferSubData(GL_ARRAY_BUFFER, 0, vtx_buffer_size, (const GLvoid*)cmd_list->VtxBuffer.Data)); + GL_CALL(glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, idx_buffer_size, (const GLvoid*)cmd_list->IdxBuffer.Data)); } else { - glBufferData(GL_ARRAY_BUFFER, vtx_buffer_size, (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx_buffer_size, (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW); + GL_CALL(glBufferData(GL_ARRAY_BUFFER, vtx_buffer_size, (const GLvoid*)cmd_list->VtxBuffer.Data, GL_STREAM_DRAW)); + GL_CALL(glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx_buffer_size, (const GLvoid*)cmd_list->IdxBuffer.Data, GL_STREAM_DRAW)); } for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) { const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; - if (pcmd->UserCallback != NULL) + if (pcmd->UserCallback != nullptr) { // User callback, registered via ImDrawList::AddCallback() // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) @@ -531,23 +554,23 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data) continue; // Apply scissor/clipping rectangle (Y is inverted in OpenGL) - glScissor((int)clip_min.x, (int)((float)fb_height - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y)); + GL_CALL(glScissor((int)clip_min.x, (int)((float)fb_height - clip_max.y), (int)(clip_max.x - clip_min.x), (int)(clip_max.y - clip_min.y))); // Bind texture, Draw - glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID()); + GL_CALL(glBindTexture(GL_TEXTURE_2D, (GLuint)(intptr_t)pcmd->GetTexID())); #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET if (bd->GlVersion >= 320) - glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset); + GL_CALL(glDrawElementsBaseVertex(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)), (GLint)pcmd->VtxOffset)); else #endif - glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx))); + GL_CALL(glDrawElements(GL_TRIANGLES, (GLsizei)pcmd->ElemCount, sizeof(ImDrawIdx) == 2 ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT, (void*)(intptr_t)(pcmd->IdxOffset * sizeof(ImDrawIdx)))); } } } // Destroy the temporary VAO #ifdef IMGUI_IMPL_OPENGL_USE_VERTEX_ARRAY - glDeleteVertexArrays(1, &vertex_array_object); + GL_CALL(glDeleteVertexArrays(1, &vertex_array_object)); #endif // Restore modified GL state @@ -600,21 +623,21 @@ bool ImGui_ImplOpenGL3_CreateFontsTexture() // Upload texture to graphics system // (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling) GLint last_texture; - glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); - glGenTextures(1, &bd->FontTexture); - glBindTexture(GL_TEXTURE_2D, bd->FontTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + GL_CALL(glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture)); + GL_CALL(glGenTextures(1, &bd->FontTexture)); + GL_CALL(glBindTexture(GL_TEXTURE_2D, bd->FontTexture)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); #ifdef GL_UNPACK_ROW_LENGTH // Not on WebGL/ES - glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + GL_CALL(glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)); #endif - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); + GL_CALL(glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels)); // Store our identifier io.Fonts->SetTexID((ImTextureID)(intptr_t)bd->FontTexture); // Restore state - glBindTexture(GL_TEXTURE_2D, last_texture); + GL_CALL(glBindTexture(GL_TEXTURE_2D, last_texture)); return true; } @@ -644,7 +667,7 @@ static bool CheckShader(GLuint handle, const char* desc) { ImVector buf; buf.resize((int)(log_length + 1)); - glGetShaderInfoLog(handle, log_length, NULL, (GLchar*)buf.begin()); + glGetShaderInfoLog(handle, log_length, nullptr, (GLchar*)buf.begin()); fprintf(stderr, "%s\n", buf.begin()); } return (GLboolean)status == GL_TRUE; @@ -663,7 +686,7 @@ static bool CheckProgram(GLuint handle, const char* desc) { ImVector buf; buf.resize((int)(log_length + 1)); - glGetProgramInfoLog(handle, log_length, NULL, (GLchar*)buf.begin()); + glGetProgramInfoLog(handle, log_length, nullptr, (GLchar*)buf.begin()); fprintf(stderr, "%s\n", buf.begin()); } return (GLboolean)status == GL_TRUE; @@ -787,8 +810,8 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects() "}\n"; // Select shaders matching our GLSL versions - const GLchar* vertex_shader = NULL; - const GLchar* fragment_shader = NULL; + const GLchar* vertex_shader = nullptr; + const GLchar* fragment_shader = nullptr; if (glsl_version < 130) { vertex_shader = vertex_shader_glsl_120; @@ -813,13 +836,13 @@ bool ImGui_ImplOpenGL3_CreateDeviceObjects() // Create shaders const GLchar* vertex_shader_with_version[2] = { bd->GlslVersionString, vertex_shader }; GLuint vert_handle = glCreateShader(GL_VERTEX_SHADER); - glShaderSource(vert_handle, 2, vertex_shader_with_version, NULL); + glShaderSource(vert_handle, 2, vertex_shader_with_version, nullptr); glCompileShader(vert_handle); CheckShader(vert_handle, "vertex shader"); const GLchar* fragment_shader_with_version[2] = { bd->GlslVersionString, fragment_shader }; GLuint frag_handle = glCreateShader(GL_FRAGMENT_SHADER); - glShaderSource(frag_handle, 2, fragment_shader_with_version, NULL); + glShaderSource(frag_handle, 2, fragment_shader_with_version, nullptr); glCompileShader(frag_handle); CheckShader(frag_handle, "fragment shader"); @@ -866,6 +889,9 @@ void ImGui_ImplOpenGL3_DestroyDeviceObjects() ImGui_ImplOpenGL3_DestroyFontsTexture(); } +#if defined(__GNUC__) +#pragma GCC diagnostic pop +#endif #if defined(__clang__) #pragma clang diagnostic pop #endif diff --git a/extern/src/imgui/imgui_impl_opengl3.h b/extern/src/imgui/imgui_impl_opengl3.h index 19f185b8..5baf5abd 100644 --- a/extern/src/imgui/imgui_impl_opengl3.h +++ b/extern/src/imgui/imgui_impl_opengl3.h @@ -13,7 +13,7 @@ // Read online: https://github.com/ocornut/imgui/tree/master/docs // About GLSL version: -// The 'glsl_version' initialization parameter should be NULL (default) or a "#version XXX" string. +// The 'glsl_version' initialization parameter should be nullptr (default) or a "#version XXX" string. // On computer platform the GLSL version default to "#version 130". On OpenGL ES 3 platform it defaults to "#version 300 es" // Only override if your GL version doesn't handle this GLSL version. See GLSL version table at the top of imgui_impl_opengl3.cpp. @@ -21,7 +21,7 @@ #include "imgui.h" // IMGUI_IMPL_API // Backend API -IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = NULL); +IMGUI_IMPL_API bool ImGui_ImplOpenGL3_Init(const char* glsl_version = nullptr); IMGUI_IMPL_API void ImGui_ImplOpenGL3_Shutdown(); IMGUI_IMPL_API void ImGui_ImplOpenGL3_NewFrame(); IMGUI_IMPL_API void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data); diff --git a/extern/src/imgui/imgui_impl_opengl3_loader.h b/extern/src/imgui/imgui_impl_opengl3_loader.h index d5762359..5b6615e9 100644 --- a/extern/src/imgui/imgui_impl_opengl3_loader.h +++ b/extern/src/imgui/imgui_impl_opengl3_loader.h @@ -9,6 +9,14 @@ // YOU SHOULD NOT NEED TO INCLUDE/USE THIS DIRECTLY. THIS IS USED BY imgui_impl_opengl3.cpp ONLY. // THE REST OF YOUR APP SHOULD USE A DIFFERENT GL LOADER: ANY GL LOADER OF YOUR CHOICE. // +// IF YOU GET BUILD ERRORS IN THIS FILE (commonly macro redefinitions or function redefinitions): +// IT LIKELY MEANS THAT YOU ARE BUILDING 'imgui_impl_opengl3.cpp' OR INCUDING 'imgui_impl_opengl3_loader.h' +// IN THE SAME COMPILATION UNIT AS ONE OF YOUR FILE WHICH IS USING A THIRD-PARTY OPENGL LOADER. +// (e.g. COULD HAPPEN IF YOU ARE DOING A UNITY/JUMBO BUILD, OR INCLUDING .CPP FILES FROM OTHERS) +// YOU SHOULD NOT BUILD BOTH IN THE SAME COMPILATION UNIT. +// BUT IF YOU REALLY WANT TO, you can '#define IMGUI_IMPL_OPENGL_LOADER_CUSTOM' and imgui_impl_opengl3.cpp +// WILL NOT BE USING OUR LOADER, AND INSTEAD EXPECT ANOTHER/YOUR LOADER TO BE AVAILABLE IN THE COMPILATION UNIT. +// // Regenerate with: // python gl3w_gen.py --output ../imgui/backends/imgui_impl_opengl3_loader.h --ref ../imgui/backends/imgui_impl_opengl3.cpp ./extra_symbols.txt // diff --git a/extern/src/imgui/imgui_impl_vulkan.cpp b/extern/src/imgui/imgui_impl_vulkan.cpp new file mode 100644 index 00000000..cd35fd9a --- /dev/null +++ b/extern/src/imgui/imgui_impl_vulkan.cpp @@ -0,0 +1,1450 @@ +// dear imgui: Renderer Backend for Vulkan +// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) + +// Implemented features: +// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. +// [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions. + +// Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'. +// This is because we need ImTextureID to carry a 64-bit value and by default ImTextureID is defined as void*. +// To build this on 32-bit systems and support texture changes: +// - [Solution 1] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'ImTextureID=ImU64' (this is what we do in our .vcxproj files) +// - [Solution 2] IDE/msbuild: in "Properties/C++/Preprocessor Definitions" add 'IMGUI_USER_CONFIG="my_imgui_config.h"' and inside 'my_imgui_config.h' add '#define ImTextureID ImU64' and as many other options as you like. +// - [Solution 3] IDE/msbuild: edit imconfig.h and add '#define ImTextureID ImU64' (prefer solution 2 to create your own config file!) +// - [Solution 4] command-line: add '/D ImTextureID=ImU64' to your cl.exe command-line (this is what we do in our batch files) + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. +// Read online: https://github.com/ocornut/imgui/tree/master/docs + +// The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification. +// IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/ + +// Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app. +// - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h. +// You will use those if you want to use this rendering backend in your engine/app. +// - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by +// the backend itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code. +// Read comments in imgui_impl_vulkan.h. + +// CHANGELOG +// (minor and older changes stripped away, please see git history for details) +// 2023-01-02: Vulkan: Fixed sampler passed to ImGui_ImplVulkan_AddTexture() not being honored + removed a bunch of duplicate code. +// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11. +// 2022-10-04: Vulkan: Added experimental ImGui_ImplVulkan_RemoveTexture() for api symetry. (#914, #5738). +// 2022-01-20: Vulkan: Added support for ImTextureID as VkDescriptorSet. User need to call ImGui_ImplVulkan_AddTexture(). Building for 32-bit targets requires '#define ImTextureID ImU64'. (#914). +// 2021-10-15: Vulkan: Call vkCmdSetScissor() at the end of render a full-viewport to reduce likehood of issues with people using VK_DYNAMIC_STATE_SCISSOR in their app without calling vkCmdSetScissor() explicitly every frame. +// 2021-06-29: Reorganized backend to pull data from a single structure to facilitate usage with multiple-contexts (all g_XXXX access changed to bd->XXXX). +// 2021-03-22: Vulkan: Fix mapped memory validation error when buffer sizes are not multiple of VkPhysicalDeviceLimits::nonCoherentAtomSize. +// 2021-02-18: Vulkan: Change blending equation to preserve alpha in output buffer. +// 2021-01-27: Vulkan: Added support for custom function load and IMGUI_IMPL_VULKAN_NO_PROTOTYPES by using ImGui_ImplVulkan_LoadFunctions(). +// 2020-11-11: Vulkan: Added support for specifying which subpass to reference during VkPipeline creation. +// 2020-09-07: Vulkan: Added VkPipeline parameter to ImGui_ImplVulkan_RenderDrawData (default to one passed to ImGui_ImplVulkan_Init). +// 2020-05-04: Vulkan: Fixed crash if initial frame has no vertices. +// 2020-04-26: Vulkan: Fixed edge case where render callbacks wouldn't be called if the ImDrawData didn't have vertices. +// 2019-08-01: Vulkan: Added support for specifying multisample count. Set ImGui_ImplVulkan_InitInfo::MSAASamples to one of the VkSampleCountFlagBits values to use, default is non-multisampled as before. +// 2019-05-29: Vulkan: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag. +// 2019-04-30: Vulkan: Added support for special ImDrawCallback_ResetRenderState callback to reset render state. +// 2019-04-04: *BREAKING CHANGE*: Vulkan: Added ImageCount/MinImageCount fields in ImGui_ImplVulkan_InitInfo, required for initialization (was previously a hard #define IMGUI_VK_QUEUED_FRAMES 2). Added ImGui_ImplVulkan_SetMinImageCount(). +// 2019-04-04: Vulkan: Added VkInstance argument to ImGui_ImplVulkanH_CreateWindow() optional helper. +// 2019-04-04: Vulkan: Avoid passing negative coordinates to vkCmdSetScissor, which debug validation layers do not like. +// 2019-04-01: Vulkan: Support for 32-bit index buffer (#define ImDrawIdx unsigned int). +// 2019-02-16: Vulkan: Viewport and clipping rectangles correctly using draw_data->FramebufferScale to allow retina display. +// 2018-11-30: Misc: Setting up io.BackendRendererName so it can be displayed in the About Window. +// 2018-08-25: Vulkan: Fixed mishandled VkSurfaceCapabilitiesKHR::maxImageCount=0 case. +// 2018-06-22: Inverted the parameters to ImGui_ImplVulkan_RenderDrawData() to be consistent with other backends. +// 2018-06-08: Misc: Extracted imgui_impl_vulkan.cpp/.h away from the old combined GLFW+Vulkan example. +// 2018-06-08: Vulkan: Use draw_data->DisplayPos and draw_data->DisplaySize to setup projection matrix and clipping rectangle. +// 2018-03-03: Vulkan: Various refactor, created a couple of ImGui_ImplVulkanH_XXX helper that the example can use and that viewport support will use. +// 2018-03-01: Vulkan: Renamed ImGui_ImplVulkan_Init_Info to ImGui_ImplVulkan_InitInfo and fields to match more closely Vulkan terminology. +// 2018-02-16: Misc: Obsoleted the io.RenderDrawListsFn callback, ImGui_ImplVulkan_Render() calls ImGui_ImplVulkan_RenderDrawData() itself. +// 2018-02-06: Misc: Removed call to ImGui::Shutdown() which is not available from 1.60 WIP, user needs to call CreateContext/DestroyContext themselves. +// 2017-05-15: Vulkan: Fix scissor offset being negative. Fix new Vulkan validation warnings. Set required depth member for buffer image copy. +// 2016-11-13: Vulkan: Fix validation layer warnings and errors and redeclare gl_PerVertex. +// 2016-10-18: Vulkan: Add location decorators & change to use structs as in/out in glsl, update embedded spv (produced with glslangValidator -x). Null the released resources. +// 2016-08-27: Vulkan: Fix Vulkan example for use when a depth buffer is active. + +#include "imgui_impl_vulkan.h" +#include + +// Visual Studio warnings +#ifdef _MSC_VER +#pragma warning (disable: 4127) // condition expression is constant +#endif + +// Reusable buffers used for rendering 1 current in-flight frame, for ImGui_ImplVulkan_RenderDrawData() +// [Please zero-clear before use!] +struct ImGui_ImplVulkanH_FrameRenderBuffers +{ + VkDeviceMemory VertexBufferMemory; + VkDeviceMemory IndexBufferMemory; + VkDeviceSize VertexBufferSize; + VkDeviceSize IndexBufferSize; + VkBuffer VertexBuffer; + VkBuffer IndexBuffer; +}; + +// Each viewport will hold 1 ImGui_ImplVulkanH_WindowRenderBuffers +// [Please zero-clear before use!] +struct ImGui_ImplVulkanH_WindowRenderBuffers +{ + uint32_t Index; + uint32_t Count; + ImGui_ImplVulkanH_FrameRenderBuffers* FrameRenderBuffers; +}; + +// Vulkan data +struct ImGui_ImplVulkan_Data +{ + ImGui_ImplVulkan_InitInfo VulkanInitInfo; + VkRenderPass RenderPass; + VkDeviceSize BufferMemoryAlignment; + VkPipelineCreateFlags PipelineCreateFlags; + VkDescriptorSetLayout DescriptorSetLayout; + VkPipelineLayout PipelineLayout; + VkPipeline Pipeline; + uint32_t Subpass; + VkShaderModule ShaderModuleVert; + VkShaderModule ShaderModuleFrag; + + // Font data + VkSampler FontSampler; + VkDeviceMemory FontMemory; + VkImage FontImage; + VkImageView FontView; + VkDescriptorSet FontDescriptorSet; + VkDeviceMemory UploadBufferMemory; + VkBuffer UploadBuffer; + + // Render buffers + ImGui_ImplVulkanH_WindowRenderBuffers MainWindowRenderBuffers; + + ImGui_ImplVulkan_Data() + { + memset((void*)this, 0, sizeof(*this)); + BufferMemoryAlignment = 256; + } +}; + +// Forward Declarations +bool ImGui_ImplVulkan_CreateDeviceObjects(); +void ImGui_ImplVulkan_DestroyDeviceObjects(); +void ImGui_ImplVulkanH_DestroyFrame(VkDevice device, ImGui_ImplVulkanH_Frame* fd, const VkAllocationCallbacks* allocator); +void ImGui_ImplVulkanH_DestroyFrameSemaphores(VkDevice device, ImGui_ImplVulkanH_FrameSemaphores* fsd, const VkAllocationCallbacks* allocator); +void ImGui_ImplVulkanH_DestroyFrameRenderBuffers(VkDevice device, ImGui_ImplVulkanH_FrameRenderBuffers* buffers, const VkAllocationCallbacks* allocator); +void ImGui_ImplVulkanH_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulkanH_WindowRenderBuffers* buffers, const VkAllocationCallbacks* allocator); +void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count); +void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator); + +// Vulkan prototypes for use with custom loaders +// (see description of IMGUI_IMPL_VULKAN_NO_PROTOTYPES in imgui_impl_vulkan.h +#ifdef VK_NO_PROTOTYPES +static bool g_FunctionsLoaded = false; +#else +static bool g_FunctionsLoaded = true; +#endif +#ifdef VK_NO_PROTOTYPES +#define IMGUI_VULKAN_FUNC_MAP(IMGUI_VULKAN_FUNC_MAP_MACRO) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkAllocateCommandBuffers) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkAllocateDescriptorSets) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkAllocateMemory) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkBindBufferMemory) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkBindImageMemory) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBindDescriptorSets) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBindIndexBuffer) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBindPipeline) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdBindVertexBuffers) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdCopyBufferToImage) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdDrawIndexed) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdPipelineBarrier) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdPushConstants) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdSetScissor) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCmdSetViewport) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateBuffer) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateCommandPool) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateDescriptorSetLayout) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateFence) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateFramebuffer) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateGraphicsPipelines) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateImage) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateImageView) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreatePipelineLayout) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateRenderPass) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateSampler) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateSemaphore) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateShaderModule) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkCreateSwapchainKHR) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyBuffer) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyCommandPool) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyDescriptorSetLayout) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyFence) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyFramebuffer) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyImage) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyImageView) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyPipeline) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyPipelineLayout) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyRenderPass) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroySampler) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroySemaphore) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroyShaderModule) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroySurfaceKHR) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDestroySwapchainKHR) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkDeviceWaitIdle) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkFlushMappedMemoryRanges) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkFreeCommandBuffers) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkFreeDescriptorSets) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkFreeMemory) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetBufferMemoryRequirements) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetImageMemoryRequirements) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceMemoryProperties) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfaceCapabilitiesKHR) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfaceFormatsKHR) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetPhysicalDeviceSurfacePresentModesKHR) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkGetSwapchainImagesKHR) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkMapMemory) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkUnmapMemory) \ + IMGUI_VULKAN_FUNC_MAP_MACRO(vkUpdateDescriptorSets) + +// Define function pointers +#define IMGUI_VULKAN_FUNC_DEF(func) static PFN_##func func; +IMGUI_VULKAN_FUNC_MAP(IMGUI_VULKAN_FUNC_DEF) +#undef IMGUI_VULKAN_FUNC_DEF +#endif // VK_NO_PROTOTYPES + +//----------------------------------------------------------------------------- +// SHADERS +//----------------------------------------------------------------------------- + +// glsl_shader.vert, compiled with: +// # glslangValidator -V -x -o glsl_shader.vert.u32 glsl_shader.vert +/* +#version 450 core +layout(location = 0) in vec2 aPos; +layout(location = 1) in vec2 aUV; +layout(location = 2) in vec4 aColor; +layout(push_constant) uniform uPushConstant { vec2 uScale; vec2 uTranslate; } pc; + +out gl_PerVertex { vec4 gl_Position; }; +layout(location = 0) out struct { vec4 Color; vec2 UV; } Out; + +void main() +{ + Out.Color = aColor; + Out.UV = aUV; + gl_Position = vec4(aPos * pc.uScale + pc.uTranslate, 0, 1); +} +*/ +static uint32_t __glsl_shader_vert_spv[] = +{ + 0x07230203,0x00010000,0x00080001,0x0000002e,0x00000000,0x00020011,0x00000001,0x0006000b, + 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001, + 0x000a000f,0x00000000,0x00000004,0x6e69616d,0x00000000,0x0000000b,0x0000000f,0x00000015, + 0x0000001b,0x0000001c,0x00030003,0x00000002,0x000001c2,0x00040005,0x00000004,0x6e69616d, + 0x00000000,0x00030005,0x00000009,0x00000000,0x00050006,0x00000009,0x00000000,0x6f6c6f43, + 0x00000072,0x00040006,0x00000009,0x00000001,0x00005655,0x00030005,0x0000000b,0x0074754f, + 0x00040005,0x0000000f,0x6c6f4361,0x0000726f,0x00030005,0x00000015,0x00565561,0x00060005, + 0x00000019,0x505f6c67,0x65567265,0x78657472,0x00000000,0x00060006,0x00000019,0x00000000, + 0x505f6c67,0x7469736f,0x006e6f69,0x00030005,0x0000001b,0x00000000,0x00040005,0x0000001c, + 0x736f5061,0x00000000,0x00060005,0x0000001e,0x73755075,0x6e6f4368,0x6e617473,0x00000074, + 0x00050006,0x0000001e,0x00000000,0x61635375,0x0000656c,0x00060006,0x0000001e,0x00000001, + 0x61725475,0x616c736e,0x00006574,0x00030005,0x00000020,0x00006370,0x00040047,0x0000000b, + 0x0000001e,0x00000000,0x00040047,0x0000000f,0x0000001e,0x00000002,0x00040047,0x00000015, + 0x0000001e,0x00000001,0x00050048,0x00000019,0x00000000,0x0000000b,0x00000000,0x00030047, + 0x00000019,0x00000002,0x00040047,0x0000001c,0x0000001e,0x00000000,0x00050048,0x0000001e, + 0x00000000,0x00000023,0x00000000,0x00050048,0x0000001e,0x00000001,0x00000023,0x00000008, + 0x00030047,0x0000001e,0x00000002,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002, + 0x00030016,0x00000006,0x00000020,0x00040017,0x00000007,0x00000006,0x00000004,0x00040017, + 0x00000008,0x00000006,0x00000002,0x0004001e,0x00000009,0x00000007,0x00000008,0x00040020, + 0x0000000a,0x00000003,0x00000009,0x0004003b,0x0000000a,0x0000000b,0x00000003,0x00040015, + 0x0000000c,0x00000020,0x00000001,0x0004002b,0x0000000c,0x0000000d,0x00000000,0x00040020, + 0x0000000e,0x00000001,0x00000007,0x0004003b,0x0000000e,0x0000000f,0x00000001,0x00040020, + 0x00000011,0x00000003,0x00000007,0x0004002b,0x0000000c,0x00000013,0x00000001,0x00040020, + 0x00000014,0x00000001,0x00000008,0x0004003b,0x00000014,0x00000015,0x00000001,0x00040020, + 0x00000017,0x00000003,0x00000008,0x0003001e,0x00000019,0x00000007,0x00040020,0x0000001a, + 0x00000003,0x00000019,0x0004003b,0x0000001a,0x0000001b,0x00000003,0x0004003b,0x00000014, + 0x0000001c,0x00000001,0x0004001e,0x0000001e,0x00000008,0x00000008,0x00040020,0x0000001f, + 0x00000009,0x0000001e,0x0004003b,0x0000001f,0x00000020,0x00000009,0x00040020,0x00000021, + 0x00000009,0x00000008,0x0004002b,0x00000006,0x00000028,0x00000000,0x0004002b,0x00000006, + 0x00000029,0x3f800000,0x00050036,0x00000002,0x00000004,0x00000000,0x00000003,0x000200f8, + 0x00000005,0x0004003d,0x00000007,0x00000010,0x0000000f,0x00050041,0x00000011,0x00000012, + 0x0000000b,0x0000000d,0x0003003e,0x00000012,0x00000010,0x0004003d,0x00000008,0x00000016, + 0x00000015,0x00050041,0x00000017,0x00000018,0x0000000b,0x00000013,0x0003003e,0x00000018, + 0x00000016,0x0004003d,0x00000008,0x0000001d,0x0000001c,0x00050041,0x00000021,0x00000022, + 0x00000020,0x0000000d,0x0004003d,0x00000008,0x00000023,0x00000022,0x00050085,0x00000008, + 0x00000024,0x0000001d,0x00000023,0x00050041,0x00000021,0x00000025,0x00000020,0x00000013, + 0x0004003d,0x00000008,0x00000026,0x00000025,0x00050081,0x00000008,0x00000027,0x00000024, + 0x00000026,0x00050051,0x00000006,0x0000002a,0x00000027,0x00000000,0x00050051,0x00000006, + 0x0000002b,0x00000027,0x00000001,0x00070050,0x00000007,0x0000002c,0x0000002a,0x0000002b, + 0x00000028,0x00000029,0x00050041,0x00000011,0x0000002d,0x0000001b,0x0000000d,0x0003003e, + 0x0000002d,0x0000002c,0x000100fd,0x00010038 +}; + +// glsl_shader.frag, compiled with: +// # glslangValidator -V -x -o glsl_shader.frag.u32 glsl_shader.frag +/* +#version 450 core +layout(location = 0) out vec4 fColor; +layout(set=0, binding=0) uniform sampler2D sTexture; +layout(location = 0) in struct { vec4 Color; vec2 UV; } In; +void main() +{ + fColor = In.Color * texture(sTexture, In.UV.st); +} +*/ +static uint32_t __glsl_shader_frag_spv[] = +{ + 0x07230203,0x00010000,0x00080001,0x0000001e,0x00000000,0x00020011,0x00000001,0x0006000b, + 0x00000001,0x4c534c47,0x6474732e,0x3035342e,0x00000000,0x0003000e,0x00000000,0x00000001, + 0x0007000f,0x00000004,0x00000004,0x6e69616d,0x00000000,0x00000009,0x0000000d,0x00030010, + 0x00000004,0x00000007,0x00030003,0x00000002,0x000001c2,0x00040005,0x00000004,0x6e69616d, + 0x00000000,0x00040005,0x00000009,0x6c6f4366,0x0000726f,0x00030005,0x0000000b,0x00000000, + 0x00050006,0x0000000b,0x00000000,0x6f6c6f43,0x00000072,0x00040006,0x0000000b,0x00000001, + 0x00005655,0x00030005,0x0000000d,0x00006e49,0x00050005,0x00000016,0x78655473,0x65727574, + 0x00000000,0x00040047,0x00000009,0x0000001e,0x00000000,0x00040047,0x0000000d,0x0000001e, + 0x00000000,0x00040047,0x00000016,0x00000022,0x00000000,0x00040047,0x00000016,0x00000021, + 0x00000000,0x00020013,0x00000002,0x00030021,0x00000003,0x00000002,0x00030016,0x00000006, + 0x00000020,0x00040017,0x00000007,0x00000006,0x00000004,0x00040020,0x00000008,0x00000003, + 0x00000007,0x0004003b,0x00000008,0x00000009,0x00000003,0x00040017,0x0000000a,0x00000006, + 0x00000002,0x0004001e,0x0000000b,0x00000007,0x0000000a,0x00040020,0x0000000c,0x00000001, + 0x0000000b,0x0004003b,0x0000000c,0x0000000d,0x00000001,0x00040015,0x0000000e,0x00000020, + 0x00000001,0x0004002b,0x0000000e,0x0000000f,0x00000000,0x00040020,0x00000010,0x00000001, + 0x00000007,0x00090019,0x00000013,0x00000006,0x00000001,0x00000000,0x00000000,0x00000000, + 0x00000001,0x00000000,0x0003001b,0x00000014,0x00000013,0x00040020,0x00000015,0x00000000, + 0x00000014,0x0004003b,0x00000015,0x00000016,0x00000000,0x0004002b,0x0000000e,0x00000018, + 0x00000001,0x00040020,0x00000019,0x00000001,0x0000000a,0x00050036,0x00000002,0x00000004, + 0x00000000,0x00000003,0x000200f8,0x00000005,0x00050041,0x00000010,0x00000011,0x0000000d, + 0x0000000f,0x0004003d,0x00000007,0x00000012,0x00000011,0x0004003d,0x00000014,0x00000017, + 0x00000016,0x00050041,0x00000019,0x0000001a,0x0000000d,0x00000018,0x0004003d,0x0000000a, + 0x0000001b,0x0000001a,0x00050057,0x00000007,0x0000001c,0x00000017,0x0000001b,0x00050085, + 0x00000007,0x0000001d,0x00000012,0x0000001c,0x0003003e,0x00000009,0x0000001d,0x000100fd, + 0x00010038 +}; + +//----------------------------------------------------------------------------- +// FUNCTIONS +//----------------------------------------------------------------------------- + +// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts +// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts. +// FIXME: multi-context support is not tested and probably dysfunctional in this backend. +static ImGui_ImplVulkan_Data* ImGui_ImplVulkan_GetBackendData() +{ + return ImGui::GetCurrentContext() ? (ImGui_ImplVulkan_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; +} + +static uint32_t ImGui_ImplVulkan_MemoryType(VkMemoryPropertyFlags properties, uint32_t type_bits) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + VkPhysicalDeviceMemoryProperties prop; + vkGetPhysicalDeviceMemoryProperties(v->PhysicalDevice, &prop); + for (uint32_t i = 0; i < prop.memoryTypeCount; i++) + if ((prop.memoryTypes[i].propertyFlags & properties) == properties && type_bits & (1 << i)) + return i; + return 0xFFFFFFFF; // Unable to find memoryType +} + +static void check_vk_result(VkResult err) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + if (!bd) + return; + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + if (v->CheckVkResultFn) + v->CheckVkResultFn(err); +} + +static void CreateOrResizeBuffer(VkBuffer& buffer, VkDeviceMemory& buffer_memory, VkDeviceSize& p_buffer_size, size_t new_size, VkBufferUsageFlagBits usage) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + VkResult err; + if (buffer != VK_NULL_HANDLE) + vkDestroyBuffer(v->Device, buffer, v->Allocator); + if (buffer_memory != VK_NULL_HANDLE) + vkFreeMemory(v->Device, buffer_memory, v->Allocator); + + VkDeviceSize vertex_buffer_size_aligned = ((new_size - 1) / bd->BufferMemoryAlignment + 1) * bd->BufferMemoryAlignment; + VkBufferCreateInfo buffer_info = {}; + buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_info.size = vertex_buffer_size_aligned; + buffer_info.usage = usage; + buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + err = vkCreateBuffer(v->Device, &buffer_info, v->Allocator, &buffer); + check_vk_result(err); + + VkMemoryRequirements req; + vkGetBufferMemoryRequirements(v->Device, buffer, &req); + bd->BufferMemoryAlignment = (bd->BufferMemoryAlignment > req.alignment) ? bd->BufferMemoryAlignment : req.alignment; + VkMemoryAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = req.size; + alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); + err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &buffer_memory); + check_vk_result(err); + + err = vkBindBufferMemory(v->Device, buffer, buffer_memory, 0); + check_vk_result(err); + p_buffer_size = req.size; +} + +static void ImGui_ImplVulkan_SetupRenderState(ImDrawData* draw_data, VkPipeline pipeline, VkCommandBuffer command_buffer, ImGui_ImplVulkanH_FrameRenderBuffers* rb, int fb_width, int fb_height) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + + // Bind pipeline: + { + vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline); + } + + // Bind Vertex And Index Buffer: + if (draw_data->TotalVtxCount > 0) + { + VkBuffer vertex_buffers[1] = { rb->VertexBuffer }; + VkDeviceSize vertex_offset[1] = { 0 }; + vkCmdBindVertexBuffers(command_buffer, 0, 1, vertex_buffers, vertex_offset); + vkCmdBindIndexBuffer(command_buffer, rb->IndexBuffer, 0, sizeof(ImDrawIdx) == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32); + } + + // Setup viewport: + { + VkViewport viewport; + viewport.x = 0; + viewport.y = 0; + viewport.width = (float)fb_width; + viewport.height = (float)fb_height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(command_buffer, 0, 1, &viewport); + } + + // Setup scale and translation: + // Our visible imgui space lies from draw_data->DisplayPps (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayPos is (0,0) for single viewport apps. + { + float scale[2]; + scale[0] = 2.0f / draw_data->DisplaySize.x; + scale[1] = 2.0f / draw_data->DisplaySize.y; + float translate[2]; + translate[0] = -1.0f - draw_data->DisplayPos.x * scale[0]; + translate[1] = -1.0f - draw_data->DisplayPos.y * scale[1]; + vkCmdPushConstants(command_buffer, bd->PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 0, sizeof(float) * 2, scale); + vkCmdPushConstants(command_buffer, bd->PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, sizeof(float) * 2, sizeof(float) * 2, translate); + } +} + +// Render function +void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline) +{ + // Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates) + int fb_width = (int)(draw_data->DisplaySize.x * draw_data->FramebufferScale.x); + int fb_height = (int)(draw_data->DisplaySize.y * draw_data->FramebufferScale.y); + if (fb_width <= 0 || fb_height <= 0) + return; + + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + if (pipeline == VK_NULL_HANDLE) + pipeline = bd->Pipeline; + + // Allocate array to store enough vertex/index buffers + ImGui_ImplVulkanH_WindowRenderBuffers* wrb = &bd->MainWindowRenderBuffers; + if (wrb->FrameRenderBuffers == nullptr) + { + wrb->Index = 0; + wrb->Count = v->ImageCount; + wrb->FrameRenderBuffers = (ImGui_ImplVulkanH_FrameRenderBuffers*)IM_ALLOC(sizeof(ImGui_ImplVulkanH_FrameRenderBuffers) * wrb->Count); + memset(wrb->FrameRenderBuffers, 0, sizeof(ImGui_ImplVulkanH_FrameRenderBuffers) * wrb->Count); + } + IM_ASSERT(wrb->Count == v->ImageCount); + wrb->Index = (wrb->Index + 1) % wrb->Count; + ImGui_ImplVulkanH_FrameRenderBuffers* rb = &wrb->FrameRenderBuffers[wrb->Index]; + + if (draw_data->TotalVtxCount > 0) + { + // Create or resize the vertex/index buffers + size_t vertex_size = draw_data->TotalVtxCount * sizeof(ImDrawVert); + size_t index_size = draw_data->TotalIdxCount * sizeof(ImDrawIdx); + if (rb->VertexBuffer == VK_NULL_HANDLE || rb->VertexBufferSize < vertex_size) + CreateOrResizeBuffer(rb->VertexBuffer, rb->VertexBufferMemory, rb->VertexBufferSize, vertex_size, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT); + if (rb->IndexBuffer == VK_NULL_HANDLE || rb->IndexBufferSize < index_size) + CreateOrResizeBuffer(rb->IndexBuffer, rb->IndexBufferMemory, rb->IndexBufferSize, index_size, VK_BUFFER_USAGE_INDEX_BUFFER_BIT); + + // Upload vertex/index data into a single contiguous GPU buffer + ImDrawVert* vtx_dst = nullptr; + ImDrawIdx* idx_dst = nullptr; + VkResult err = vkMapMemory(v->Device, rb->VertexBufferMemory, 0, rb->VertexBufferSize, 0, (void**)(&vtx_dst)); + check_vk_result(err); + err = vkMapMemory(v->Device, rb->IndexBufferMemory, 0, rb->IndexBufferSize, 0, (void**)(&idx_dst)); + check_vk_result(err); + for (int n = 0; n < draw_data->CmdListsCount; n++) + { + const ImDrawList* cmd_list = draw_data->CmdLists[n]; + memcpy(vtx_dst, cmd_list->VtxBuffer.Data, cmd_list->VtxBuffer.Size * sizeof(ImDrawVert)); + memcpy(idx_dst, cmd_list->IdxBuffer.Data, cmd_list->IdxBuffer.Size * sizeof(ImDrawIdx)); + vtx_dst += cmd_list->VtxBuffer.Size; + idx_dst += cmd_list->IdxBuffer.Size; + } + VkMappedMemoryRange range[2] = {}; + range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + range[0].memory = rb->VertexBufferMemory; + range[0].size = VK_WHOLE_SIZE; + range[1].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + range[1].memory = rb->IndexBufferMemory; + range[1].size = VK_WHOLE_SIZE; + err = vkFlushMappedMemoryRanges(v->Device, 2, range); + check_vk_result(err); + vkUnmapMemory(v->Device, rb->VertexBufferMemory); + vkUnmapMemory(v->Device, rb->IndexBufferMemory); + } + + // Setup desired Vulkan state + ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height); + + // Will project scissor/clipping rectangles into framebuffer space + ImVec2 clip_off = draw_data->DisplayPos; // (0,0) unless using multi-viewports + ImVec2 clip_scale = draw_data->FramebufferScale; // (1,1) unless using retina display which are often (2,2) + + // Render command lists + // (Because we merged all buffers into a single one, we maintain our own offset into them) + int global_vtx_offset = 0; + int global_idx_offset = 0; + for (int n = 0; n < draw_data->CmdListsCount; n++) + { + const ImDrawList* cmd_list = draw_data->CmdLists[n]; + for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++) + { + const ImDrawCmd* pcmd = &cmd_list->CmdBuffer[cmd_i]; + if (pcmd->UserCallback != nullptr) + { + // User callback, registered via ImDrawList::AddCallback() + // (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.) + if (pcmd->UserCallback == ImDrawCallback_ResetRenderState) + ImGui_ImplVulkan_SetupRenderState(draw_data, pipeline, command_buffer, rb, fb_width, fb_height); + else + pcmd->UserCallback(cmd_list, pcmd); + } + else + { + // Project scissor/clipping rectangles into framebuffer space + ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); + ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); + + // Clamp to viewport as vkCmdSetScissor() won't accept values that are off bounds + if (clip_min.x < 0.0f) { clip_min.x = 0.0f; } + if (clip_min.y < 0.0f) { clip_min.y = 0.0f; } + if (clip_max.x > fb_width) { clip_max.x = (float)fb_width; } + if (clip_max.y > fb_height) { clip_max.y = (float)fb_height; } + if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) + continue; + + // Apply scissor/clipping rectangle + VkRect2D scissor; + scissor.offset.x = (int32_t)(clip_min.x); + scissor.offset.y = (int32_t)(clip_min.y); + scissor.extent.width = (uint32_t)(clip_max.x - clip_min.x); + scissor.extent.height = (uint32_t)(clip_max.y - clip_min.y); + vkCmdSetScissor(command_buffer, 0, 1, &scissor); + + // Bind DescriptorSet with font or user texture + VkDescriptorSet desc_set[1] = { (VkDescriptorSet)pcmd->TextureId }; + if (sizeof(ImTextureID) < sizeof(ImU64)) + { + // We don't support texture switches if ImTextureID hasn't been redefined to be 64-bit. Do a flaky check that other textures haven't been used. + IM_ASSERT(pcmd->TextureId == (ImTextureID)bd->FontDescriptorSet); + desc_set[0] = bd->FontDescriptorSet; + } + vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, bd->PipelineLayout, 0, 1, desc_set, 0, nullptr); + + // Draw + vkCmdDrawIndexed(command_buffer, pcmd->ElemCount, 1, pcmd->IdxOffset + global_idx_offset, pcmd->VtxOffset + global_vtx_offset, 0); + } + } + global_idx_offset += cmd_list->IdxBuffer.Size; + global_vtx_offset += cmd_list->VtxBuffer.Size; + } + + // Note: at this point both vkCmdSetViewport() and vkCmdSetScissor() have been called. + // Our last values will leak into user/application rendering IF: + // - Your app uses a pipeline with VK_DYNAMIC_STATE_VIEWPORT or VK_DYNAMIC_STATE_SCISSOR dynamic state + // - And you forgot to call vkCmdSetViewport() and vkCmdSetScissor() yourself to explicitly set that state. + // If you use VK_DYNAMIC_STATE_VIEWPORT or VK_DYNAMIC_STATE_SCISSOR you are responsible for setting the values before rendering. + // In theory we should aim to backup/restore those values but I am not sure this is possible. + // We perform a call to vkCmdSetScissor() to set back a full viewport which is likely to fix things for 99% users but technically this is not perfect. (See github #4644) + VkRect2D scissor = { { 0, 0 }, { (uint32_t)fb_width, (uint32_t)fb_height } }; + vkCmdSetScissor(command_buffer, 0, 1, &scissor); +} + +bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer) +{ + ImGuiIO& io = ImGui::GetIO(); + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + + unsigned char* pixels; + int width, height; + io.Fonts->GetTexDataAsRGBA32(&pixels, &width, &height); + size_t upload_size = width * height * 4 * sizeof(char); + + VkResult err; + + // Create the Image: + { + VkImageCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + info.imageType = VK_IMAGE_TYPE_2D; + info.format = VK_FORMAT_R8G8B8A8_UNORM; + info.extent.width = width; + info.extent.height = height; + info.extent.depth = 1; + info.mipLevels = 1; + info.arrayLayers = 1; + info.samples = VK_SAMPLE_COUNT_1_BIT; + info.tiling = VK_IMAGE_TILING_OPTIMAL; + info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + err = vkCreateImage(v->Device, &info, v->Allocator, &bd->FontImage); + check_vk_result(err); + VkMemoryRequirements req; + vkGetImageMemoryRequirements(v->Device, bd->FontImage, &req); + VkMemoryAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = req.size; + alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, req.memoryTypeBits); + err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &bd->FontMemory); + check_vk_result(err); + err = vkBindImageMemory(v->Device, bd->FontImage, bd->FontMemory, 0); + check_vk_result(err); + } + + // Create the Image View: + { + VkImageViewCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + info.image = bd->FontImage; + info.viewType = VK_IMAGE_VIEW_TYPE_2D; + info.format = VK_FORMAT_R8G8B8A8_UNORM; + info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + info.subresourceRange.levelCount = 1; + info.subresourceRange.layerCount = 1; + err = vkCreateImageView(v->Device, &info, v->Allocator, &bd->FontView); + check_vk_result(err); + } + + // Create the Descriptor Set: + bd->FontDescriptorSet = (VkDescriptorSet)ImGui_ImplVulkan_AddTexture(bd->FontSampler, bd->FontView, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); + + // Create the Upload Buffer: + { + VkBufferCreateInfo buffer_info = {}; + buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_info.size = upload_size; + buffer_info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + err = vkCreateBuffer(v->Device, &buffer_info, v->Allocator, &bd->UploadBuffer); + check_vk_result(err); + VkMemoryRequirements req; + vkGetBufferMemoryRequirements(v->Device, bd->UploadBuffer, &req); + bd->BufferMemoryAlignment = (bd->BufferMemoryAlignment > req.alignment) ? bd->BufferMemoryAlignment : req.alignment; + VkMemoryAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + alloc_info.allocationSize = req.size; + alloc_info.memoryTypeIndex = ImGui_ImplVulkan_MemoryType(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, req.memoryTypeBits); + err = vkAllocateMemory(v->Device, &alloc_info, v->Allocator, &bd->UploadBufferMemory); + check_vk_result(err); + err = vkBindBufferMemory(v->Device, bd->UploadBuffer, bd->UploadBufferMemory, 0); + check_vk_result(err); + } + + // Upload to Buffer: + { + char* map = nullptr; + err = vkMapMemory(v->Device, bd->UploadBufferMemory, 0, upload_size, 0, (void**)(&map)); + check_vk_result(err); + memcpy(map, pixels, upload_size); + VkMappedMemoryRange range[1] = {}; + range[0].sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + range[0].memory = bd->UploadBufferMemory; + range[0].size = upload_size; + err = vkFlushMappedMemoryRanges(v->Device, 1, range); + check_vk_result(err); + vkUnmapMemory(v->Device, bd->UploadBufferMemory); + } + + // Copy to Image: + { + VkImageMemoryBarrier copy_barrier[1] = {}; + copy_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + copy_barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + copy_barrier[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; + copy_barrier[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + copy_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + copy_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + copy_barrier[0].image = bd->FontImage; + copy_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + copy_barrier[0].subresourceRange.levelCount = 1; + copy_barrier[0].subresourceRange.layerCount = 1; + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, copy_barrier); + + VkBufferImageCopy region = {}; + region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + region.imageSubresource.layerCount = 1; + region.imageExtent.width = width; + region.imageExtent.height = height; + region.imageExtent.depth = 1; + vkCmdCopyBufferToImage(command_buffer, bd->UploadBuffer, bd->FontImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); + + VkImageMemoryBarrier use_barrier[1] = {}; + use_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; + use_barrier[0].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + use_barrier[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + use_barrier[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + use_barrier[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + use_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + use_barrier[0].image = bd->FontImage; + use_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + use_barrier[0].subresourceRange.levelCount = 1; + use_barrier[0].subresourceRange.layerCount = 1; + vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, use_barrier); + } + + // Store our identifier + io.Fonts->SetTexID((ImTextureID)bd->FontDescriptorSet); + + return true; +} + +static void ImGui_ImplVulkan_CreateShaderModules(VkDevice device, const VkAllocationCallbacks* allocator) +{ + // Create the shader modules + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + if (bd->ShaderModuleVert == VK_NULL_HANDLE) + { + VkShaderModuleCreateInfo vert_info = {}; + vert_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + vert_info.codeSize = sizeof(__glsl_shader_vert_spv); + vert_info.pCode = (uint32_t*)__glsl_shader_vert_spv; + VkResult err = vkCreateShaderModule(device, &vert_info, allocator, &bd->ShaderModuleVert); + check_vk_result(err); + } + if (bd->ShaderModuleFrag == VK_NULL_HANDLE) + { + VkShaderModuleCreateInfo frag_info = {}; + frag_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + frag_info.codeSize = sizeof(__glsl_shader_frag_spv); + frag_info.pCode = (uint32_t*)__glsl_shader_frag_spv; + VkResult err = vkCreateShaderModule(device, &frag_info, allocator, &bd->ShaderModuleFrag); + check_vk_result(err); + } +} + +static void ImGui_ImplVulkan_CreatePipeline(VkDevice device, const VkAllocationCallbacks* allocator, VkPipelineCache pipelineCache, VkRenderPass renderPass, VkSampleCountFlagBits MSAASamples, VkPipeline* pipeline, uint32_t subpass) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_CreateShaderModules(device, allocator); + + VkPipelineShaderStageCreateInfo stage[2] = {}; + stage[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stage[0].stage = VK_SHADER_STAGE_VERTEX_BIT; + stage[0].module = bd->ShaderModuleVert; + stage[0].pName = "main"; + stage[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + stage[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; + stage[1].module = bd->ShaderModuleFrag; + stage[1].pName = "main"; + + VkVertexInputBindingDescription binding_desc[1] = {}; + binding_desc[0].stride = sizeof(ImDrawVert); + binding_desc[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX; + + VkVertexInputAttributeDescription attribute_desc[3] = {}; + attribute_desc[0].location = 0; + attribute_desc[0].binding = binding_desc[0].binding; + attribute_desc[0].format = VK_FORMAT_R32G32_SFLOAT; + attribute_desc[0].offset = IM_OFFSETOF(ImDrawVert, pos); + attribute_desc[1].location = 1; + attribute_desc[1].binding = binding_desc[0].binding; + attribute_desc[1].format = VK_FORMAT_R32G32_SFLOAT; + attribute_desc[1].offset = IM_OFFSETOF(ImDrawVert, uv); + attribute_desc[2].location = 2; + attribute_desc[2].binding = binding_desc[0].binding; + attribute_desc[2].format = VK_FORMAT_R8G8B8A8_UNORM; + attribute_desc[2].offset = IM_OFFSETOF(ImDrawVert, col); + + VkPipelineVertexInputStateCreateInfo vertex_info = {}; + vertex_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + vertex_info.vertexBindingDescriptionCount = 1; + vertex_info.pVertexBindingDescriptions = binding_desc; + vertex_info.vertexAttributeDescriptionCount = 3; + vertex_info.pVertexAttributeDescriptions = attribute_desc; + + VkPipelineInputAssemblyStateCreateInfo ia_info = {}; + ia_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + ia_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + + VkPipelineViewportStateCreateInfo viewport_info = {}; + viewport_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewport_info.viewportCount = 1; + viewport_info.scissorCount = 1; + + VkPipelineRasterizationStateCreateInfo raster_info = {}; + raster_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + raster_info.polygonMode = VK_POLYGON_MODE_FILL; + raster_info.cullMode = VK_CULL_MODE_NONE; + raster_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + raster_info.lineWidth = 1.0f; + + VkPipelineMultisampleStateCreateInfo ms_info = {}; + ms_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + ms_info.rasterizationSamples = (MSAASamples != 0) ? MSAASamples : VK_SAMPLE_COUNT_1_BIT; + + VkPipelineColorBlendAttachmentState color_attachment[1] = {}; + color_attachment[0].blendEnable = VK_TRUE; + color_attachment[0].srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + color_attachment[0].dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + color_attachment[0].colorBlendOp = VK_BLEND_OP_ADD; + color_attachment[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + color_attachment[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + color_attachment[0].alphaBlendOp = VK_BLEND_OP_ADD; + color_attachment[0].colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; + + VkPipelineDepthStencilStateCreateInfo depth_info = {}; + depth_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + + VkPipelineColorBlendStateCreateInfo blend_info = {}; + blend_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + blend_info.attachmentCount = 1; + blend_info.pAttachments = color_attachment; + + VkDynamicState dynamic_states[2] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; + VkPipelineDynamicStateCreateInfo dynamic_state = {}; + dynamic_state.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamic_state.dynamicStateCount = (uint32_t)IM_ARRAYSIZE(dynamic_states); + dynamic_state.pDynamicStates = dynamic_states; + + VkGraphicsPipelineCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + info.flags = bd->PipelineCreateFlags; + info.stageCount = 2; + info.pStages = stage; + info.pVertexInputState = &vertex_info; + info.pInputAssemblyState = &ia_info; + info.pViewportState = &viewport_info; + info.pRasterizationState = &raster_info; + info.pMultisampleState = &ms_info; + info.pDepthStencilState = &depth_info; + info.pColorBlendState = &blend_info; + info.pDynamicState = &dynamic_state; + info.layout = bd->PipelineLayout; + info.renderPass = renderPass; + info.subpass = subpass; + VkResult err = vkCreateGraphicsPipelines(device, pipelineCache, 1, &info, allocator, pipeline); + check_vk_result(err); +} + +bool ImGui_ImplVulkan_CreateDeviceObjects() +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + VkResult err; + + if (!bd->FontSampler) + { + // Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling. + VkSamplerCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; + info.magFilter = VK_FILTER_LINEAR; + info.minFilter = VK_FILTER_LINEAR; + info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + info.minLod = -1000; + info.maxLod = 1000; + info.maxAnisotropy = 1.0f; + err = vkCreateSampler(v->Device, &info, v->Allocator, &bd->FontSampler); + check_vk_result(err); + } + + if (!bd->DescriptorSetLayout) + { + VkDescriptorSetLayoutBinding binding[1] = {}; + binding[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + binding[0].descriptorCount = 1; + binding[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + VkDescriptorSetLayoutCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; + info.bindingCount = 1; + info.pBindings = binding; + err = vkCreateDescriptorSetLayout(v->Device, &info, v->Allocator, &bd->DescriptorSetLayout); + check_vk_result(err); + } + + if (!bd->PipelineLayout) + { + // Constants: we are using 'vec2 offset' and 'vec2 scale' instead of a full 3d projection matrix + VkPushConstantRange push_constants[1] = {}; + push_constants[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + push_constants[0].offset = sizeof(float) * 0; + push_constants[0].size = sizeof(float) * 4; + VkDescriptorSetLayout set_layout[1] = { bd->DescriptorSetLayout }; + VkPipelineLayoutCreateInfo layout_info = {}; + layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + layout_info.setLayoutCount = 1; + layout_info.pSetLayouts = set_layout; + layout_info.pushConstantRangeCount = 1; + layout_info.pPushConstantRanges = push_constants; + err = vkCreatePipelineLayout(v->Device, &layout_info, v->Allocator, &bd->PipelineLayout); + check_vk_result(err); + } + + ImGui_ImplVulkan_CreatePipeline(v->Device, v->Allocator, v->PipelineCache, bd->RenderPass, v->MSAASamples, &bd->Pipeline, bd->Subpass); + + return true; +} + +void ImGui_ImplVulkan_DestroyFontUploadObjects() +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + if (bd->UploadBuffer) + { + vkDestroyBuffer(v->Device, bd->UploadBuffer, v->Allocator); + bd->UploadBuffer = VK_NULL_HANDLE; + } + if (bd->UploadBufferMemory) + { + vkFreeMemory(v->Device, bd->UploadBufferMemory, v->Allocator); + bd->UploadBufferMemory = VK_NULL_HANDLE; + } +} + +void ImGui_ImplVulkan_DestroyDeviceObjects() +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + ImGui_ImplVulkanH_DestroyWindowRenderBuffers(v->Device, &bd->MainWindowRenderBuffers, v->Allocator); + ImGui_ImplVulkan_DestroyFontUploadObjects(); + + if (bd->ShaderModuleVert) { vkDestroyShaderModule(v->Device, bd->ShaderModuleVert, v->Allocator); bd->ShaderModuleVert = VK_NULL_HANDLE; } + if (bd->ShaderModuleFrag) { vkDestroyShaderModule(v->Device, bd->ShaderModuleFrag, v->Allocator); bd->ShaderModuleFrag = VK_NULL_HANDLE; } + if (bd->FontView) { vkDestroyImageView(v->Device, bd->FontView, v->Allocator); bd->FontView = VK_NULL_HANDLE; } + if (bd->FontImage) { vkDestroyImage(v->Device, bd->FontImage, v->Allocator); bd->FontImage = VK_NULL_HANDLE; } + if (bd->FontMemory) { vkFreeMemory(v->Device, bd->FontMemory, v->Allocator); bd->FontMemory = VK_NULL_HANDLE; } + if (bd->FontSampler) { vkDestroySampler(v->Device, bd->FontSampler, v->Allocator); bd->FontSampler = VK_NULL_HANDLE; } + if (bd->DescriptorSetLayout) { vkDestroyDescriptorSetLayout(v->Device, bd->DescriptorSetLayout, v->Allocator); bd->DescriptorSetLayout = VK_NULL_HANDLE; } + if (bd->PipelineLayout) { vkDestroyPipelineLayout(v->Device, bd->PipelineLayout, v->Allocator); bd->PipelineLayout = VK_NULL_HANDLE; } + if (bd->Pipeline) { vkDestroyPipeline(v->Device, bd->Pipeline, v->Allocator); bd->Pipeline = VK_NULL_HANDLE; } +} + +bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data) +{ + // Load function pointers + // You can use the default Vulkan loader using: + // ImGui_ImplVulkan_LoadFunctions([](const char* function_name, void*) { return vkGetInstanceProcAddr(your_vk_isntance, function_name); }); + // But this would be equivalent to not setting VK_NO_PROTOTYPES. +#ifdef VK_NO_PROTOTYPES +#define IMGUI_VULKAN_FUNC_LOAD(func) \ + func = reinterpret_cast(loader_func(#func, user_data)); \ + if (func == nullptr) \ + return false; + IMGUI_VULKAN_FUNC_MAP(IMGUI_VULKAN_FUNC_LOAD) +#undef IMGUI_VULKAN_FUNC_LOAD +#else + IM_UNUSED(loader_func); + IM_UNUSED(user_data); +#endif + g_FunctionsLoaded = true; + return true; +} + +bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass render_pass) +{ + IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); + + ImGuiIO& io = ImGui::GetIO(); + IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); + + // Setup backend capabilities flags + ImGui_ImplVulkan_Data* bd = IM_NEW(ImGui_ImplVulkan_Data)(); + io.BackendRendererUserData = (void*)bd; + io.BackendRendererName = "imgui_impl_vulkan"; + io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes. + + IM_ASSERT(info->Instance != VK_NULL_HANDLE); + IM_ASSERT(info->PhysicalDevice != VK_NULL_HANDLE); + IM_ASSERT(info->Device != VK_NULL_HANDLE); + IM_ASSERT(info->Queue != VK_NULL_HANDLE); + IM_ASSERT(info->DescriptorPool != VK_NULL_HANDLE); + IM_ASSERT(info->MinImageCount >= 2); + IM_ASSERT(info->ImageCount >= info->MinImageCount); + IM_ASSERT(render_pass != VK_NULL_HANDLE); + + bd->VulkanInitInfo = *info; + bd->RenderPass = render_pass; + bd->Subpass = info->Subpass; + + ImGui_ImplVulkan_CreateDeviceObjects(); + + return true; +} + +void ImGui_ImplVulkan_Shutdown() +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); + ImGuiIO& io = ImGui::GetIO(); + + ImGui_ImplVulkan_DestroyDeviceObjects(); + io.BackendRendererName = nullptr; + io.BackendRendererUserData = nullptr; + IM_DELETE(bd); +} + +void ImGui_ImplVulkan_NewFrame() +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + IM_ASSERT(bd != nullptr && "Did you call ImGui_ImplVulkan_Init()?"); + IM_UNUSED(bd); +} + +void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + IM_ASSERT(min_image_count >= 2); + if (bd->VulkanInitInfo.MinImageCount == min_image_count) + return; + + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + VkResult err = vkDeviceWaitIdle(v->Device); + check_vk_result(err); + ImGui_ImplVulkanH_DestroyWindowRenderBuffers(v->Device, &bd->MainWindowRenderBuffers, v->Allocator); + bd->VulkanInitInfo.MinImageCount = min_image_count; +} + +// Register a texture +// FIXME: This is experimental in the sense that we are unsure how to best design/tackle this problem, please post to https://github.com/ocornut/imgui/pull/914 if you have suggestions. +VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + + // Create Descriptor Set: + VkDescriptorSet descriptor_set; + { + VkDescriptorSetAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + alloc_info.descriptorPool = v->DescriptorPool; + alloc_info.descriptorSetCount = 1; + alloc_info.pSetLayouts = &bd->DescriptorSetLayout; + VkResult err = vkAllocateDescriptorSets(v->Device, &alloc_info, &descriptor_set); + check_vk_result(err); + } + + // Update the Descriptor Set: + { + VkDescriptorImageInfo desc_image[1] = {}; + desc_image[0].sampler = sampler; + desc_image[0].imageView = image_view; + desc_image[0].imageLayout = image_layout; + VkWriteDescriptorSet write_desc[1] = {}; + write_desc[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + write_desc[0].dstSet = descriptor_set; + write_desc[0].descriptorCount = 1; + write_desc[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + write_desc[0].pImageInfo = desc_image; + vkUpdateDescriptorSets(v->Device, 1, write_desc, 0, nullptr); + } + return descriptor_set; +} + +void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set) +{ + ImGui_ImplVulkan_Data* bd = ImGui_ImplVulkan_GetBackendData(); + ImGui_ImplVulkan_InitInfo* v = &bd->VulkanInitInfo; + vkFreeDescriptorSets(v->Device, v->DescriptorPool, 1, &descriptor_set); +} + +//------------------------------------------------------------------------- +// Internal / Miscellaneous Vulkan Helpers +// (Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own app.) +//------------------------------------------------------------------------- +// You probably do NOT need to use or care about those functions. +// Those functions only exist because: +// 1) they facilitate the readability and maintenance of the multiple main.cpp examples files. +// 2) the upcoming multi-viewport feature will need them internally. +// Generally we avoid exposing any kind of superfluous high-level helpers in the backends, +// but it is too much code to duplicate everywhere so we exceptionally expose them. +// +// Your engine/app will likely _already_ have code to setup all that stuff (swap chain, render pass, frame buffers, etc.). +// You may read this code to learn about Vulkan, but it is recommended you use you own custom tailored code to do equivalent work. +// (The ImGui_ImplVulkanH_XXX functions do not interact with any of the state used by the regular ImGui_ImplVulkan_XXX functions) +//------------------------------------------------------------------------- + +VkSurfaceFormatKHR ImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space) +{ + IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); + IM_ASSERT(request_formats != nullptr); + IM_ASSERT(request_formats_count > 0); + + // Per Spec Format and View Format are expected to be the same unless VK_IMAGE_CREATE_MUTABLE_BIT was set at image creation + // Assuming that the default behavior is without setting this bit, there is no need for separate Swapchain image and image view format + // Additionally several new color spaces were introduced with Vulkan Spec v1.0.40, + // hence we must make sure that a format with the mostly available color space, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR, is found and used. + uint32_t avail_count; + vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &avail_count, nullptr); + ImVector avail_format; + avail_format.resize((int)avail_count); + vkGetPhysicalDeviceSurfaceFormatsKHR(physical_device, surface, &avail_count, avail_format.Data); + + // First check if only one format, VK_FORMAT_UNDEFINED, is available, which would imply that any format is available + if (avail_count == 1) + { + if (avail_format[0].format == VK_FORMAT_UNDEFINED) + { + VkSurfaceFormatKHR ret; + ret.format = request_formats[0]; + ret.colorSpace = request_color_space; + return ret; + } + else + { + // No point in searching another format + return avail_format[0]; + } + } + else + { + // Request several formats, the first found will be used + for (int request_i = 0; request_i < request_formats_count; request_i++) + for (uint32_t avail_i = 0; avail_i < avail_count; avail_i++) + if (avail_format[avail_i].format == request_formats[request_i] && avail_format[avail_i].colorSpace == request_color_space) + return avail_format[avail_i]; + + // If none of the requested image formats could be found, use the first available + return avail_format[0]; + } +} + +VkPresentModeKHR ImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count) +{ + IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); + IM_ASSERT(request_modes != nullptr); + IM_ASSERT(request_modes_count > 0); + + // Request a certain mode and confirm that it is available. If not use VK_PRESENT_MODE_FIFO_KHR which is mandatory + uint32_t avail_count = 0; + vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &avail_count, nullptr); + ImVector avail_modes; + avail_modes.resize((int)avail_count); + vkGetPhysicalDeviceSurfacePresentModesKHR(physical_device, surface, &avail_count, avail_modes.Data); + //for (uint32_t avail_i = 0; avail_i < avail_count; avail_i++) + // printf("[vulkan] avail_modes[%d] = %d\n", avail_i, avail_modes[avail_i]); + + for (int request_i = 0; request_i < request_modes_count; request_i++) + for (uint32_t avail_i = 0; avail_i < avail_count; avail_i++) + if (request_modes[request_i] == avail_modes[avail_i]) + return request_modes[request_i]; + + return VK_PRESENT_MODE_FIFO_KHR; // Always available +} + +void ImGui_ImplVulkanH_CreateWindowCommandBuffers(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator) +{ + IM_ASSERT(physical_device != VK_NULL_HANDLE && device != VK_NULL_HANDLE); + (void)physical_device; + (void)allocator; + + // Create Command Buffers + VkResult err; + for (uint32_t i = 0; i < wd->ImageCount; i++) + { + ImGui_ImplVulkanH_Frame* fd = &wd->Frames[i]; + ImGui_ImplVulkanH_FrameSemaphores* fsd = &wd->FrameSemaphores[i]; + { + VkCommandPoolCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + info.queueFamilyIndex = queue_family; + err = vkCreateCommandPool(device, &info, allocator, &fd->CommandPool); + check_vk_result(err); + } + { + VkCommandBufferAllocateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + info.commandPool = fd->CommandPool; + info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + info.commandBufferCount = 1; + err = vkAllocateCommandBuffers(device, &info, &fd->CommandBuffer); + check_vk_result(err); + } + { + VkFenceCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + info.flags = VK_FENCE_CREATE_SIGNALED_BIT; + err = vkCreateFence(device, &info, allocator, &fd->Fence); + check_vk_result(err); + } + { + VkSemaphoreCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + err = vkCreateSemaphore(device, &info, allocator, &fsd->ImageAcquiredSemaphore); + check_vk_result(err); + err = vkCreateSemaphore(device, &info, allocator, &fsd->RenderCompleteSemaphore); + check_vk_result(err); + } + } +} + +int ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_mode) +{ + if (present_mode == VK_PRESENT_MODE_MAILBOX_KHR) + return 3; + if (present_mode == VK_PRESENT_MODE_FIFO_KHR || present_mode == VK_PRESENT_MODE_FIFO_RELAXED_KHR) + return 2; + if (present_mode == VK_PRESENT_MODE_IMMEDIATE_KHR) + return 1; + IM_ASSERT(0); + return 1; +} + +// Also destroy old swap chain and in-flight frames data, if any. +void ImGui_ImplVulkanH_CreateWindowSwapChain(VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count) +{ + VkResult err; + VkSwapchainKHR old_swapchain = wd->Swapchain; + wd->Swapchain = VK_NULL_HANDLE; + err = vkDeviceWaitIdle(device); + check_vk_result(err); + + // We don't use ImGui_ImplVulkanH_DestroyWindow() because we want to preserve the old swapchain to create the new one. + // Destroy old Framebuffer + for (uint32_t i = 0; i < wd->ImageCount; i++) + { + ImGui_ImplVulkanH_DestroyFrame(device, &wd->Frames[i], allocator); + ImGui_ImplVulkanH_DestroyFrameSemaphores(device, &wd->FrameSemaphores[i], allocator); + } + IM_FREE(wd->Frames); + IM_FREE(wd->FrameSemaphores); + wd->Frames = nullptr; + wd->FrameSemaphores = nullptr; + wd->ImageCount = 0; + if (wd->RenderPass) + vkDestroyRenderPass(device, wd->RenderPass, allocator); + if (wd->Pipeline) + vkDestroyPipeline(device, wd->Pipeline, allocator); + + // If min image count was not specified, request different count of images dependent on selected present mode + if (min_image_count == 0) + min_image_count = ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(wd->PresentMode); + + // Create Swapchain + { + VkSwapchainCreateInfoKHR info = {}; + info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + info.surface = wd->Surface; + info.minImageCount = min_image_count; + info.imageFormat = wd->SurfaceFormat.format; + info.imageColorSpace = wd->SurfaceFormat.colorSpace; + info.imageArrayLayers = 1; + info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; // Assume that graphics family == present family + info.preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; + info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + info.presentMode = wd->PresentMode; + info.clipped = VK_TRUE; + info.oldSwapchain = old_swapchain; + VkSurfaceCapabilitiesKHR cap; + err = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, wd->Surface, &cap); + check_vk_result(err); + if (info.minImageCount < cap.minImageCount) + info.minImageCount = cap.minImageCount; + else if (cap.maxImageCount != 0 && info.minImageCount > cap.maxImageCount) + info.minImageCount = cap.maxImageCount; + + if (cap.currentExtent.width == 0xffffffff) + { + info.imageExtent.width = wd->Width = w; + info.imageExtent.height = wd->Height = h; + } + else + { + info.imageExtent.width = wd->Width = cap.currentExtent.width; + info.imageExtent.height = wd->Height = cap.currentExtent.height; + } + err = vkCreateSwapchainKHR(device, &info, allocator, &wd->Swapchain); + check_vk_result(err); + err = vkGetSwapchainImagesKHR(device, wd->Swapchain, &wd->ImageCount, nullptr); + check_vk_result(err); + VkImage backbuffers[16] = {}; + IM_ASSERT(wd->ImageCount >= min_image_count); + IM_ASSERT(wd->ImageCount < IM_ARRAYSIZE(backbuffers)); + err = vkGetSwapchainImagesKHR(device, wd->Swapchain, &wd->ImageCount, backbuffers); + check_vk_result(err); + + IM_ASSERT(wd->Frames == nullptr); + wd->Frames = (ImGui_ImplVulkanH_Frame*)IM_ALLOC(sizeof(ImGui_ImplVulkanH_Frame) * wd->ImageCount); + wd->FrameSemaphores = (ImGui_ImplVulkanH_FrameSemaphores*)IM_ALLOC(sizeof(ImGui_ImplVulkanH_FrameSemaphores) * wd->ImageCount); + memset(wd->Frames, 0, sizeof(wd->Frames[0]) * wd->ImageCount); + memset(wd->FrameSemaphores, 0, sizeof(wd->FrameSemaphores[0]) * wd->ImageCount); + for (uint32_t i = 0; i < wd->ImageCount; i++) + wd->Frames[i].Backbuffer = backbuffers[i]; + } + if (old_swapchain) + vkDestroySwapchainKHR(device, old_swapchain, allocator); + + // Create the Render Pass + { + VkAttachmentDescription attachment = {}; + attachment.format = wd->SurfaceFormat.format; + attachment.samples = VK_SAMPLE_COUNT_1_BIT; + attachment.loadOp = wd->ClearEnable ? VK_ATTACHMENT_LOAD_OP_CLEAR : VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + VkAttachmentReference color_attachment = {}; + color_attachment.attachment = 0; + color_attachment.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + VkSubpassDescription subpass = {}; + subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass.colorAttachmentCount = 1; + subpass.pColorAttachments = &color_attachment; + VkSubpassDependency dependency = {}; + dependency.srcSubpass = VK_SUBPASS_EXTERNAL; + dependency.dstSubpass = 0; + dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + dependency.srcAccessMask = 0; + dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; + VkRenderPassCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + info.attachmentCount = 1; + info.pAttachments = &attachment; + info.subpassCount = 1; + info.pSubpasses = &subpass; + info.dependencyCount = 1; + info.pDependencies = &dependency; + err = vkCreateRenderPass(device, &info, allocator, &wd->RenderPass); + check_vk_result(err); + + // We do not create a pipeline by default as this is also used by examples' main.cpp, + // but secondary viewport in multi-viewport mode may want to create one with: + //ImGui_ImplVulkan_CreatePipeline(device, allocator, VK_NULL_HANDLE, wd->RenderPass, VK_SAMPLE_COUNT_1_BIT, &wd->Pipeline, bd->Subpass); + } + + // Create The Image Views + { + VkImageViewCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + info.viewType = VK_IMAGE_VIEW_TYPE_2D; + info.format = wd->SurfaceFormat.format; + info.components.r = VK_COMPONENT_SWIZZLE_R; + info.components.g = VK_COMPONENT_SWIZZLE_G; + info.components.b = VK_COMPONENT_SWIZZLE_B; + info.components.a = VK_COMPONENT_SWIZZLE_A; + VkImageSubresourceRange image_range = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; + info.subresourceRange = image_range; + for (uint32_t i = 0; i < wd->ImageCount; i++) + { + ImGui_ImplVulkanH_Frame* fd = &wd->Frames[i]; + info.image = fd->Backbuffer; + err = vkCreateImageView(device, &info, allocator, &fd->BackbufferView); + check_vk_result(err); + } + } + + // Create Framebuffer + { + VkImageView attachment[1]; + VkFramebufferCreateInfo info = {}; + info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + info.renderPass = wd->RenderPass; + info.attachmentCount = 1; + info.pAttachments = attachment; + info.width = wd->Width; + info.height = wd->Height; + info.layers = 1; + for (uint32_t i = 0; i < wd->ImageCount; i++) + { + ImGui_ImplVulkanH_Frame* fd = &wd->Frames[i]; + attachment[0] = fd->BackbufferView; + err = vkCreateFramebuffer(device, &info, allocator, &fd->Framebuffer); + check_vk_result(err); + } + } +} + +// Create or resize window +void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int width, int height, uint32_t min_image_count) +{ + IM_ASSERT(g_FunctionsLoaded && "Need to call ImGui_ImplVulkan_LoadFunctions() if IMGUI_IMPL_VULKAN_NO_PROTOTYPES or VK_NO_PROTOTYPES are set!"); + (void)instance; + ImGui_ImplVulkanH_CreateWindowSwapChain(physical_device, device, wd, allocator, width, height, min_image_count); + ImGui_ImplVulkanH_CreateWindowCommandBuffers(physical_device, device, wd, queue_family, allocator); +} + +void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wd, const VkAllocationCallbacks* allocator) +{ + vkDeviceWaitIdle(device); // FIXME: We could wait on the Queue if we had the queue in wd-> (otherwise VulkanH functions can't use globals) + //vkQueueWaitIdle(bd->Queue); + + for (uint32_t i = 0; i < wd->ImageCount; i++) + { + ImGui_ImplVulkanH_DestroyFrame(device, &wd->Frames[i], allocator); + ImGui_ImplVulkanH_DestroyFrameSemaphores(device, &wd->FrameSemaphores[i], allocator); + } + IM_FREE(wd->Frames); + IM_FREE(wd->FrameSemaphores); + wd->Frames = nullptr; + wd->FrameSemaphores = nullptr; + vkDestroyPipeline(device, wd->Pipeline, allocator); + vkDestroyRenderPass(device, wd->RenderPass, allocator); + vkDestroySwapchainKHR(device, wd->Swapchain, allocator); + vkDestroySurfaceKHR(instance, wd->Surface, allocator); + + *wd = ImGui_ImplVulkanH_Window(); +} + +void ImGui_ImplVulkanH_DestroyFrame(VkDevice device, ImGui_ImplVulkanH_Frame* fd, const VkAllocationCallbacks* allocator) +{ + vkDestroyFence(device, fd->Fence, allocator); + vkFreeCommandBuffers(device, fd->CommandPool, 1, &fd->CommandBuffer); + vkDestroyCommandPool(device, fd->CommandPool, allocator); + fd->Fence = VK_NULL_HANDLE; + fd->CommandBuffer = VK_NULL_HANDLE; + fd->CommandPool = VK_NULL_HANDLE; + + vkDestroyImageView(device, fd->BackbufferView, allocator); + vkDestroyFramebuffer(device, fd->Framebuffer, allocator); +} + +void ImGui_ImplVulkanH_DestroyFrameSemaphores(VkDevice device, ImGui_ImplVulkanH_FrameSemaphores* fsd, const VkAllocationCallbacks* allocator) +{ + vkDestroySemaphore(device, fsd->ImageAcquiredSemaphore, allocator); + vkDestroySemaphore(device, fsd->RenderCompleteSemaphore, allocator); + fsd->ImageAcquiredSemaphore = fsd->RenderCompleteSemaphore = VK_NULL_HANDLE; +} + +void ImGui_ImplVulkanH_DestroyFrameRenderBuffers(VkDevice device, ImGui_ImplVulkanH_FrameRenderBuffers* buffers, const VkAllocationCallbacks* allocator) +{ + if (buffers->VertexBuffer) { vkDestroyBuffer(device, buffers->VertexBuffer, allocator); buffers->VertexBuffer = VK_NULL_HANDLE; } + if (buffers->VertexBufferMemory) { vkFreeMemory(device, buffers->VertexBufferMemory, allocator); buffers->VertexBufferMemory = VK_NULL_HANDLE; } + if (buffers->IndexBuffer) { vkDestroyBuffer(device, buffers->IndexBuffer, allocator); buffers->IndexBuffer = VK_NULL_HANDLE; } + if (buffers->IndexBufferMemory) { vkFreeMemory(device, buffers->IndexBufferMemory, allocator); buffers->IndexBufferMemory = VK_NULL_HANDLE; } + buffers->VertexBufferSize = 0; + buffers->IndexBufferSize = 0; +} + +void ImGui_ImplVulkanH_DestroyWindowRenderBuffers(VkDevice device, ImGui_ImplVulkanH_WindowRenderBuffers* buffers, const VkAllocationCallbacks* allocator) +{ + for (uint32_t n = 0; n < buffers->Count; n++) + ImGui_ImplVulkanH_DestroyFrameRenderBuffers(device, &buffers->FrameRenderBuffers[n], allocator); + IM_FREE(buffers->FrameRenderBuffers); + buffers->FrameRenderBuffers = nullptr; + buffers->Index = 0; + buffers->Count = 0; +} diff --git a/extern/src/imgui/imgui_impl_vulkan.h b/extern/src/imgui/imgui_impl_vulkan.h new file mode 100644 index 00000000..99fd0082 --- /dev/null +++ b/extern/src/imgui/imgui_impl_vulkan.h @@ -0,0 +1,157 @@ +// dear imgui: Renderer Backend for Vulkan +// This needs to be used along with a Platform Backend (e.g. GLFW, SDL, Win32, custom..) + +// Implemented features: +// [X] Renderer: Support for large meshes (64k+ vertices) with 16-bit indices. +// [!] Renderer: User texture binding. Use 'VkDescriptorSet' as ImTextureID. Read the FAQ about ImTextureID! See https://github.com/ocornut/imgui/pull/914 for discussions. + +// Important: on 32-bit systems, user texture binding is only supported if your imconfig file has '#define ImTextureID ImU64'. +// See imgui_impl_vulkan.cpp file for details. + +// You can use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this. +// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need. +// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp. +// Read online: https://github.com/ocornut/imgui/tree/master/docs + +// The aim of imgui_impl_vulkan.h/.cpp is to be usable in your engine without any modification. +// IF YOU FEEL YOU NEED TO MAKE ANY CHANGE TO THIS CODE, please share them and your feedback at https://github.com/ocornut/imgui/ + +// Important note to the reader who wish to integrate imgui_impl_vulkan.cpp/.h in their own engine/app. +// - Common ImGui_ImplVulkan_XXX functions and structures are used to interface with imgui_impl_vulkan.cpp/.h. +// You will use those if you want to use this rendering backend in your engine/app. +// - Helper ImGui_ImplVulkanH_XXX functions and structures are only used by this example (main.cpp) and by +// the backend itself (imgui_impl_vulkan.cpp), but should PROBABLY NOT be used by your own engine/app code. +// Read comments in imgui_impl_vulkan.h. + +#pragma once +#include "imgui.h" // IMGUI_IMPL_API + +// [Configuration] in order to use a custom Vulkan function loader: +// (1) You'll need to disable default Vulkan function prototypes. +// We provide a '#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES' convenience configuration flag. +// In order to make sure this is visible from the imgui_impl_vulkan.cpp compilation unit: +// - Add '#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES' in your imconfig.h file +// - Or as a compilation flag in your build system +// - Or uncomment here (not recommended because you'd be modifying imgui sources!) +// - Do not simply add it in a .cpp file! +// (2) Call ImGui_ImplVulkan_LoadFunctions() before ImGui_ImplVulkan_Init() with your custom function. +// If you have no idea what this is, leave it alone! +//#define IMGUI_IMPL_VULKAN_NO_PROTOTYPES + +// Vulkan includes +#if defined(IMGUI_IMPL_VULKAN_NO_PROTOTYPES) && !defined(VK_NO_PROTOTYPES) +#define VK_NO_PROTOTYPES +#endif +#include + +// Initialization data, for ImGui_ImplVulkan_Init() +// [Please zero-clear before use!] +struct ImGui_ImplVulkan_InitInfo +{ + VkInstance Instance; + VkPhysicalDevice PhysicalDevice; + VkDevice Device; + uint32_t QueueFamily; + VkQueue Queue; + VkPipelineCache PipelineCache; + VkDescriptorPool DescriptorPool; + uint32_t Subpass; + uint32_t MinImageCount; // >= 2 + uint32_t ImageCount; // >= MinImageCount + VkSampleCountFlagBits MSAASamples; // >= VK_SAMPLE_COUNT_1_BIT (0 -> default to VK_SAMPLE_COUNT_1_BIT) + const VkAllocationCallbacks* Allocator; + void (*CheckVkResultFn)(VkResult err); +}; + +// Called by user code +IMGUI_IMPL_API bool ImGui_ImplVulkan_Init(ImGui_ImplVulkan_InitInfo* info, VkRenderPass render_pass); +IMGUI_IMPL_API void ImGui_ImplVulkan_Shutdown(); +IMGUI_IMPL_API void ImGui_ImplVulkan_NewFrame(); +IMGUI_IMPL_API void ImGui_ImplVulkan_RenderDrawData(ImDrawData* draw_data, VkCommandBuffer command_buffer, VkPipeline pipeline = VK_NULL_HANDLE); +IMGUI_IMPL_API bool ImGui_ImplVulkan_CreateFontsTexture(VkCommandBuffer command_buffer); +IMGUI_IMPL_API void ImGui_ImplVulkan_DestroyFontUploadObjects(); +IMGUI_IMPL_API void ImGui_ImplVulkan_SetMinImageCount(uint32_t min_image_count); // To override MinImageCount after initialization (e.g. if swap chain is recreated) + +// Register a texture (VkDescriptorSet == ImTextureID) +// FIXME: This is experimental in the sense that we are unsure how to best design/tackle this problem +// Please post to https://github.com/ocornut/imgui/pull/914 if you have suggestions. +IMGUI_IMPL_API VkDescriptorSet ImGui_ImplVulkan_AddTexture(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout); +IMGUI_IMPL_API void ImGui_ImplVulkan_RemoveTexture(VkDescriptorSet descriptor_set); + +// Optional: load Vulkan functions with a custom function loader +// This is only useful with IMGUI_IMPL_VULKAN_NO_PROTOTYPES / VK_NO_PROTOTYPES +IMGUI_IMPL_API bool ImGui_ImplVulkan_LoadFunctions(PFN_vkVoidFunction(*loader_func)(const char* function_name, void* user_data), void* user_data = nullptr); + +//------------------------------------------------------------------------- +// Internal / Miscellaneous Vulkan Helpers +// (Used by example's main.cpp. Used by multi-viewport features. PROBABLY NOT used by your own engine/app.) +//------------------------------------------------------------------------- +// You probably do NOT need to use or care about those functions. +// Those functions only exist because: +// 1) they facilitate the readability and maintenance of the multiple main.cpp examples files. +// 2) the upcoming multi-viewport feature will need them internally. +// Generally we avoid exposing any kind of superfluous high-level helpers in the backends, +// but it is too much code to duplicate everywhere so we exceptionally expose them. +// +// Your engine/app will likely _already_ have code to setup all that stuff (swap chain, render pass, frame buffers, etc.). +// You may read this code to learn about Vulkan, but it is recommended you use you own custom tailored code to do equivalent work. +// (The ImGui_ImplVulkanH_XXX functions do not interact with any of the state used by the regular ImGui_ImplVulkan_XXX functions) +//------------------------------------------------------------------------- + +struct ImGui_ImplVulkanH_Frame; +struct ImGui_ImplVulkanH_Window; + +// Helpers +IMGUI_IMPL_API void ImGui_ImplVulkanH_CreateOrResizeWindow(VkInstance instance, VkPhysicalDevice physical_device, VkDevice device, ImGui_ImplVulkanH_Window* wnd, uint32_t queue_family, const VkAllocationCallbacks* allocator, int w, int h, uint32_t min_image_count); +IMGUI_IMPL_API void ImGui_ImplVulkanH_DestroyWindow(VkInstance instance, VkDevice device, ImGui_ImplVulkanH_Window* wnd, const VkAllocationCallbacks* allocator); +IMGUI_IMPL_API VkSurfaceFormatKHR ImGui_ImplVulkanH_SelectSurfaceFormat(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkFormat* request_formats, int request_formats_count, VkColorSpaceKHR request_color_space); +IMGUI_IMPL_API VkPresentModeKHR ImGui_ImplVulkanH_SelectPresentMode(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const VkPresentModeKHR* request_modes, int request_modes_count); +IMGUI_IMPL_API int ImGui_ImplVulkanH_GetMinImageCountFromPresentMode(VkPresentModeKHR present_mode); + +// Helper structure to hold the data needed by one rendering frame +// (Used by example's main.cpp. Used by multi-viewport features. Probably NOT used by your own engine/app.) +// [Please zero-clear before use!] +struct ImGui_ImplVulkanH_Frame +{ + VkCommandPool CommandPool; + VkCommandBuffer CommandBuffer; + VkFence Fence; + VkImage Backbuffer; + VkImageView BackbufferView; + VkFramebuffer Framebuffer; +}; + +struct ImGui_ImplVulkanH_FrameSemaphores +{ + VkSemaphore ImageAcquiredSemaphore; + VkSemaphore RenderCompleteSemaphore; +}; + +// Helper structure to hold the data needed by one rendering context into one OS window +// (Used by example's main.cpp. Used by multi-viewport features. Probably NOT used by your own engine/app.) +struct ImGui_ImplVulkanH_Window +{ + int Width; + int Height; + VkSwapchainKHR Swapchain; + VkSurfaceKHR Surface; + VkSurfaceFormatKHR SurfaceFormat; + VkPresentModeKHR PresentMode; + VkRenderPass RenderPass; + VkPipeline Pipeline; // The window pipeline may uses a different VkRenderPass than the one passed in ImGui_ImplVulkan_InitInfo + bool ClearEnable; + VkClearValue ClearValue; + uint32_t FrameIndex; // Current frame being rendered to (0 <= FrameIndex < FrameInFlightCount) + uint32_t ImageCount; // Number of simultaneous in-flight frames (returned by vkGetSwapchainImagesKHR, usually derived from min_image_count) + uint32_t SemaphoreIndex; // Current set of swapchain wait semaphores we're using (needs to be distinct from per frame data) + ImGui_ImplVulkanH_Frame* Frames; + ImGui_ImplVulkanH_FrameSemaphores* FrameSemaphores; + + ImGui_ImplVulkanH_Window() + { + memset((void*)this, 0, sizeof(*this)); + PresentMode = (VkPresentModeKHR)~0; // Ensure we get an error if user doesn't set this. + ClearEnable = true; + } +}; + diff --git a/extern/src/imgui/imgui_internal.h b/extern/src/imgui/imgui_internal.h index 81d41fd7..e57eab77 100644 --- a/extern/src/imgui/imgui_internal.h +++ b/extern/src/imgui/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.88 +// dear imgui, v1.89.2 // (internal structures/api) // You may use this file to debug, understand or extend ImGui features but we don't provide any guarantee of forward compatibility! @@ -26,6 +26,7 @@ Index of this file: // [SECTION] Docking support // [SECTION] Viewport support // [SECTION] Settings support +// [SECTION] Localization support // [SECTION] Metrics, Debug tools // [SECTION] Generic context hooks // [SECTION] ImGuiContext (main imgui context) @@ -55,7 +56,7 @@ Index of this file: #include // INT_MIN, INT_MAX // Enable SSE intrinsics if available -#if (defined __SSE__ || defined __x86_64__ || defined _M_X64) && !defined(IMGUI_DISABLE_SSE) +#if (defined __SSE__ || defined __x86_64__ || defined _M_X64 || (defined(_M_IX86_FP) && (_M_IX86_FP >= 1))) && !defined(IMGUI_DISABLE_SSE) #define IMGUI_ENABLE_SSE #include #endif @@ -121,6 +122,7 @@ struct ImGuiDataTypeInfo; // Type information associated to a ImGuiDat struct ImGuiGroupData; // Stacked storage data for BeginGroup()/EndGroup() struct ImGuiInputTextState; // Internal state of the currently focused/edited text input box struct ImGuiLastItemData; // Status storage for last submitted items +struct ImGuiLocEntry; // A localization entry. struct ImGuiMenuColumns; // Simple column measurement, currently used for MenuItem() only struct ImGuiNavItemData; // Result of a gamepad/keyboard directional navigation move query result struct ImGuiMetricsConfig; // Storage for ShowMetricsWindow() and DebugNodeXXX() functions @@ -144,15 +146,19 @@ struct ImGuiWindow; // Storage for one window struct ImGuiWindowTempData; // Temporary storage for one window (that's the data which in theory we could ditch at the end of the frame, in practice we currently keep it for each window) struct ImGuiWindowSettings; // Storage for a window .ini settings (we keep one of those even if the actual window wasn't instanced during this session) +// Enumerations // Use your programming IDE "Go to definition" facility on the names of the center columns to find the actual flags/enum lists. +enum ImGuiLocKey : int; // -> enum ImGuiLocKey // Enum: a localization entry for translation. typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // Enum: Horizontal or vertical + +// Flags typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later) typedef int ImGuiDebugLogFlags; // -> enum ImGuiDebugLogFlags_ // Flags: for ShowDebugLogWindow(), g.DebugLogFlags -typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag() -typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for DC.LastItemStatusFlags +typedef int ImGuiInputFlags; // -> enum ImGuiInputFlags_ // Flags: for IsKeyPressed(), IsMouseClicked(), SetKeyOwner(), SetItemKeyOwner() etc. +typedef int ImGuiItemFlags; // -> enum ImGuiItemFlags_ // Flags: for PushItemFlag(), g.LastItemData.InFlags +typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for g.LastItemData.StatusFlags typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns() typedef int ImGuiNavHighlightFlags; // -> enum ImGuiNavHighlightFlags_ // Flags: for RenderNavHighlight() -typedef int ImGuiNavDirSourceFlags; // -> enum ImGuiNavDirSourceFlags_ // Flags: for GetNavInputAmount2d() typedef int ImGuiNavMoveFlags; // -> enum ImGuiNavMoveFlags_ // Flags: for navigation requests typedef int ImGuiNextItemDataFlags; // -> enum ImGuiNextItemDataFlags_ // Flags: for SetNextItemXXX() functions typedef int ImGuiNextWindowDataFlags; // -> enum ImGuiNextWindowDataFlags_// Flags: for SetNextWindowXXX() functions @@ -200,16 +206,21 @@ namespace ImStb #ifndef IMGUI_DISABLE_DEFAULT_FORMAT_FUNCTIONS #define IMGUI_DEBUG_PRINTF(_FMT,...) printf(_FMT, __VA_ARGS__) #else -#define IMGUI_DEBUG_PRINTF(_FMT,...) +#define IMGUI_DEBUG_PRINTF(_FMT,...) ((void)0) #endif #endif // Debug Logging for ShowDebugLogWindow(). This is designed for relatively rare events so please don't spam. -#define IMGUI_DEBUG_LOG(...) ImGui::DebugLog(__VA_ARGS__); +#ifndef IMGUI_DISABLE_DEBUG_TOOLS +#define IMGUI_DEBUG_LOG(...) ImGui::DebugLog(__VA_ARGS__) +#else +#define IMGUI_DEBUG_LOG(...) ((void)0) +#endif #define IMGUI_DEBUG_LOG_ACTIVEID(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventActiveId) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_FOCUS(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFocus) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_POPUP(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_NAV(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventNav) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) +#define IMGUI_DEBUG_LOG_CLIPPER(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventClipper) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_IO(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventIO) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) // Static Asserts @@ -237,7 +248,9 @@ namespace ImStb #else #define IM_NEWLINE "\n" #endif +#ifndef IM_TABSIZE // Until we move this to runtime and/or add proper tab support, at least allow users to compile-time override #define IM_TABSIZE (4) +#endif #define IM_MEMALIGN(_OFF,_ALIGN) (((_OFF) + ((_ALIGN) - 1)) & ~((_ALIGN) - 1)) // Memory align e.g. IM_ALIGN(0,4)=0, IM_ALIGN(1,4)=4, IM_ALIGN(4,4)=4, IM_ALIGN(5,4)=8 #define IM_F32_TO_INT8_UNBOUND(_VAL) ((int)((_VAL) * 255.0f + ((_VAL)>=0 ? 0.5f : -0.5f))) // Unsaturated, for display purpose #define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255 @@ -299,6 +312,7 @@ namespace ImStb // - Helper: ImSpan<>, ImSpanAllocator<> // - Helper: ImPool<> // - Helper: ImChunkStream<> +// - Helper: ImGuiTextIndex //----------------------------------------------------------------------------- // Helpers: Hashing @@ -331,8 +345,11 @@ IMGUI_API const ImWchar*ImStrbolW(const ImWchar* buf_mid_line, const ImWchar* bu IMGUI_API const char* ImStristr(const char* haystack, const char* haystack_end, const char* needle, const char* needle_end); IMGUI_API void ImStrTrimBlanks(char* str); IMGUI_API const char* ImStrSkipBlank(const char* str); +IM_MSVC_RUNTIME_CHECKS_OFF +static inline char ImToUpper(char c) { return (c >= 'a' && c <= 'z') ? c &= ~32 : c; } static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; } static inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c == '\t' || c == 0x3000; } +IM_MSVC_RUNTIME_CHECKS_RESTORE // Helpers: Formatting IMGUI_API int ImFormatString(char* buf, size_t buf_size, const char* fmt, ...) IM_FMTARGS(3); @@ -460,6 +477,7 @@ static inline ImVec2 ImRotate(const ImVec2& v, float cos_a, float sin_a) static inline float ImLinearSweep(float current, float target, float speed) { if (current < target) return ImMin(current + speed, target); if (current > target) return ImMax(current - speed, target); return current; } static inline ImVec2 ImMul(const ImVec2& lhs, const ImVec2& rhs) { return ImVec2(lhs.x * rhs.x, lhs.y * rhs.y); } static inline bool ImIsFloatAboveGuaranteedIntegerPrecision(float f) { return f <= -16777216 || f >= 16777216; } +static inline float ImExponentialMovingAverage(float avg, float sample, int n) { avg -= avg / n; avg += sample / n; return avg; } IM_MSVC_RUNTIME_CHECKS_RESTORE // Helpers: Geometry @@ -688,6 +706,20 @@ struct ImChunkStream }; +// Helper: ImGuiTextIndex<> +// Maintain a line index for a text buffer. This is a strong candidate to be moved into the public API. +struct ImGuiTextIndex +{ + ImVector LineOffsets; + int EndOffset = 0; // Because we don't own text buffer we need to maintain EndOffset (may bake in LineOffsets?) + + void clear() { LineOffsets.clear(); EndOffset = 0; } + int size() { return LineOffsets.Size; } + const char* get_line_begin(const char* base, int n) { return base + LineOffsets[n]; } + const char* get_line_end(const char* base, int n) { return base + (n + 1 < LineOffsets.Size ? (LineOffsets[n + 1] - 1) : EndOffset); } + void append(const char* base, int old_size, int new_size); +}; + //----------------------------------------------------------------------------- // [SECTION] ImDrawList support //----------------------------------------------------------------------------- @@ -729,6 +761,9 @@ struct IMGUI_API ImDrawListSharedData ImVec4 ClipRectFullscreen; // Value for PushClipRectFullscreen() ImDrawListFlags InitialFlags; // Initial flags at the beginning of the frame (it is possible to alter flags on a per-drawlist basis afterwards) + // [Internal] Temp write buffer + ImVector TempBuffer; + // [Internal] Lookup tables ImVec2 ArcFastVtx[IM_DRAWLIST_ARCFAST_TABLE_SIZE]; // Sample points on the quarter of the circle. float ArcFastRadiusCutoff; // Cutoff radius after which arc drawing will fallback to slower PathArcTo() @@ -753,10 +788,14 @@ struct ImDrawDataBuilder // [SECTION] Widgets support: flags, enums, data structures //----------------------------------------------------------------------------- -// Transient per-window flags, reset at the beginning of the frame. For child window, inherited from parent on first Begin(). +// Flags used by upcoming items +// - input: PushItemFlag() manipulates g.CurrentItemFlags, ItemAdd() calls may add extra flags. +// - output: stored in g.LastItemData.InFlags +// Current window shared by all windows. // This is going to be exposed in imgui.h when stabilized enough. enum ImGuiItemFlags_ { + // Controlled by user ImGuiItemFlags_None = 0, ImGuiItemFlags_NoTabStop = 1 << 0, // false // Disable keyboard tabbing (FIXME: should merge with _NoNav) ImGuiItemFlags_ButtonRepeat = 1 << 1, // false // Button() will return true multiple times based on io.KeyRepeatDelay and io.KeyRepeatRate settings. @@ -766,10 +805,14 @@ enum ImGuiItemFlags_ ImGuiItemFlags_SelectableDontClosePopup = 1 << 5, // false // Disable MenuItem/Selectable() automatically closing their popup window ImGuiItemFlags_MixedValue = 1 << 6, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets) ImGuiItemFlags_ReadOnly = 1 << 7, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. - ImGuiItemFlags_Inputable = 1 << 8 // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature. + ImGuiItemFlags_NoWindowHoverableCheck = 1 << 8, // false // Disable hoverable check in ItemHoverable() + + // Controlled by widget code + ImGuiItemFlags_Inputable = 1 << 10, // false // [WIP] Auto-activate input mode when tab focused. Currently only used and supported by a few items before it becomes a generic feature. }; -// Storage for LastItem data +// Status flags for an already submitted item +// - output: stored in g.LastItemData.StatusFlags enum ImGuiItemStatusFlags_ { ImGuiItemStatusFlags_None = 0, @@ -781,14 +824,14 @@ enum ImGuiItemStatusFlags_ ImGuiItemStatusFlags_HasDeactivated = 1 << 5, // Set if the widget/group is able to provide data for the ImGuiItemStatusFlags_Deactivated flag. ImGuiItemStatusFlags_Deactivated = 1 << 6, // Only valid if ImGuiItemStatusFlags_HasDeactivated is set. ImGuiItemStatusFlags_HoveredWindow = 1 << 7, // Override the HoveredWindow test to allow cross-window hover testing. - ImGuiItemStatusFlags_FocusedByTabbing = 1 << 8 // Set when the Focusable item just got focused by Tabbing (FIXME: to be removed soon) + ImGuiItemStatusFlags_FocusedByTabbing = 1 << 8, // Set when the Focusable item just got focused by Tabbing (FIXME: to be removed soon) + ImGuiItemStatusFlags_Visible = 1 << 9, // [WIP] Set when item is overlapping the current clipping rectangle (Used internally. Please don't use yet: API/system will change as we refactor Itemadd()). #ifdef IMGUI_ENABLE_TEST_ENGINE - , // [imgui_tests only] ImGuiItemStatusFlags_Openable = 1 << 20, // Item is an openable (e.g. TreeNode) ImGuiItemStatusFlags_Opened = 1 << 21, // ImGuiItemStatusFlags_Checkable = 1 << 22, // Item is a checkable (e.g. CheckBox, MenuItem) - ImGuiItemStatusFlags_Checked = 1 << 23 // + ImGuiItemStatusFlags_Checked = 1 << 23, // #endif }; @@ -798,7 +841,7 @@ enum ImGuiInputTextFlagsPrivate_ // [Internal] ImGuiInputTextFlags_Multiline = 1 << 26, // For internal use by InputTextMultiline() ImGuiInputTextFlags_NoMarkEdited = 1 << 27, // For internal use by functions using InputText() before reformatting data - ImGuiInputTextFlags_MergedItem = 1 << 28 // For internal use by TempInputText(), will skip calling ItemAdd(). Require bounding-box to strictly match. + ImGuiInputTextFlags_MergedItem = 1 << 28, // For internal use by TempInputText(), will skip calling ItemAdd(). Require bounding-box to strictly match. }; // Extend ImGuiButtonFlags_ @@ -818,23 +861,25 @@ enum ImGuiButtonFlagsPrivate_ ImGuiButtonFlags_AlignTextBaseLine = 1 << 15, // vertically align button to match text baseline - ButtonEx() only // FIXME: Should be removed and handled by SmallButton(), not possible currently because of DC.CursorPosPrevLine ImGuiButtonFlags_NoKeyModifiers = 1 << 16, // disable mouse interaction if a key modifier is held ImGuiButtonFlags_NoHoldingActiveId = 1 << 17, // don't set ActiveId while holding the mouse (ImGuiButtonFlags_PressedOnClick only) - ImGuiButtonFlags_NoNavFocus = 1 << 18, // don't override navigation focus when activated + ImGuiButtonFlags_NoNavFocus = 1 << 18, // don't override navigation focus when activated (FIXME: this is essentially used everytime an item uses ImGuiItemFlags_NoNav, but because legacy specs don't requires LastItemData to be set ButtonBehavior(), we can't poll g.LastItemData.InFlags) ImGuiButtonFlags_NoHoveredOnFocus = 1 << 19, // don't report as hovered when nav focus is on this item + ImGuiButtonFlags_NoSetKeyOwner = 1 << 20, // don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!) + ImGuiButtonFlags_NoTestKeyOwner = 1 << 21, // don't test key/input owner when polling the key (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!) ImGuiButtonFlags_PressedOnMask_ = ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere | ImGuiButtonFlags_PressedOnRelease | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_PressedOnDragDropHold, - ImGuiButtonFlags_PressedOnDefault_ = ImGuiButtonFlags_PressedOnClickRelease + ImGuiButtonFlags_PressedOnDefault_ = ImGuiButtonFlags_PressedOnClickRelease, }; // Extend ImGuiComboFlags_ enum ImGuiComboFlagsPrivate_ { - ImGuiComboFlags_CustomPreview = 1 << 20 // enable BeginComboPreview() + ImGuiComboFlags_CustomPreview = 1 << 20, // enable BeginComboPreview() }; // Extend ImGuiSliderFlags_ enum ImGuiSliderFlagsPrivate_ { ImGuiSliderFlags_Vertical = 1 << 20, // Should this slider be orientated vertically? - ImGuiSliderFlags_ReadOnly = 1 << 21 + ImGuiSliderFlags_ReadOnly = 1 << 21, }; // Extend ImGuiSelectableFlags_ @@ -846,35 +891,35 @@ enum ImGuiSelectableFlagsPrivate_ ImGuiSelectableFlags_SelectOnClick = 1 << 22, // Override button behavior to react on Click (default is Click+Release) ImGuiSelectableFlags_SelectOnRelease = 1 << 23, // Override button behavior to react on Release (default is Click+Release) ImGuiSelectableFlags_SpanAvailWidth = 1 << 24, // Span all avail width even if we declared less for layout purpose. FIXME: We may be able to remove this (added in 6251d379, 2bcafc86 for menus) - ImGuiSelectableFlags_DrawHoveredWhenHeld = 1 << 25, // Always show active when held, even is not hovered. This concept could probably be renamed/formalized somehow. - ImGuiSelectableFlags_SetNavIdOnHover = 1 << 26, // Set Nav/Focus ID on mouse hover (used by MenuItem) - ImGuiSelectableFlags_NoPadWithHalfSpacing = 1 << 27 // Disable padding each side with ItemSpacing * 0.5f + ImGuiSelectableFlags_SetNavIdOnHover = 1 << 25, // Set Nav/Focus ID on mouse hover (used by MenuItem) + ImGuiSelectableFlags_NoPadWithHalfSpacing = 1 << 26, // Disable padding each side with ItemSpacing * 0.5f + ImGuiSelectableFlags_NoSetKeyOwner = 1 << 27, // Don't set key/input owner on the initial click (note: mouse buttons are keys! often, the key in question will be ImGuiKey_MouseLeft!) }; // Extend ImGuiTreeNodeFlags_ enum ImGuiTreeNodeFlagsPrivate_ { - ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 20 + ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 20, }; enum ImGuiSeparatorFlags_ { - ImGuiSeparatorFlags_None = 0, - ImGuiSeparatorFlags_Horizontal = 1 << 0, // Axis default to current layout type, so generally Horizontal unless e.g. in a menu bar - ImGuiSeparatorFlags_Vertical = 1 << 1, - ImGuiSeparatorFlags_SpanAllColumns = 1 << 2 + ImGuiSeparatorFlags_None = 0, + ImGuiSeparatorFlags_Horizontal = 1 << 0, // Axis default to current layout type, so generally Horizontal unless e.g. in a menu bar + ImGuiSeparatorFlags_Vertical = 1 << 1, + ImGuiSeparatorFlags_SpanAllColumns = 1 << 2, }; enum ImGuiTextFlags_ { - ImGuiTextFlags_None = 0, - ImGuiTextFlags_NoWidthForLargeClippedText = 1 << 0 + ImGuiTextFlags_None = 0, + ImGuiTextFlags_NoWidthForLargeClippedText = 1 << 0, }; enum ImGuiTooltipFlags_ { - ImGuiTooltipFlags_None = 0, - ImGuiTooltipFlags_OverridePreviousTooltip = 1 << 0 // Override will clear/ignore previously submitted tooltip (defaults to append) + ImGuiTooltipFlags_None = 0, + ImGuiTooltipFlags_OverridePreviousTooltip = 1 << 0, // Override will clear/ignore previously submitted tooltip (defaults to append) }; // FIXME: this is in development, not exposed/functional as a generic feature yet. @@ -891,7 +936,7 @@ enum ImGuiLogType ImGuiLogType_TTY, ImGuiLogType_File, ImGuiLogType_Buffer, - ImGuiLogType_Clipboard + ImGuiLogType_Clipboard, }; // X/Y enums are fixed to 0/1 so they may be used to index ImVec2 @@ -905,14 +950,14 @@ enum ImGuiAxis enum ImGuiPlotType { ImGuiPlotType_Lines, - ImGuiPlotType_Histogram + ImGuiPlotType_Histogram, }; enum ImGuiPopupPositionPolicy { ImGuiPopupPositionPolicy_Default, ImGuiPopupPositionPolicy_ComboBox, - ImGuiPopupPositionPolicy_Tooltip + ImGuiPopupPositionPolicy_Tooltip, }; struct ImGuiDataTypeTempStorage @@ -934,7 +979,7 @@ enum ImGuiDataTypePrivate_ { ImGuiDataType_String = ImGuiDataType_COUNT + 1, ImGuiDataType_Pointer, - ImGuiDataType_ID + ImGuiDataType_ID, }; // Stacked color modifier, backup of modified data so we can restore it @@ -1005,6 +1050,7 @@ struct IMGUI_API ImGuiMenuColumns // For a given item ID, access with ImGui::GetInputTextState() struct IMGUI_API ImGuiInputTextState { + ImGuiContext* Ctx; // parent dear imgui context ImGuiID ID; // widget id owning the text state int CurLenW, CurLenA; // we need to maintain our buffer length in both UTF-8 and wchar format. UTF-8 length is valid even if TextA is not. ImVector TextW; // edit buffer, we need to persist but can't guarantee the persistence of the user-provided buffer. so we copy into own buffer. @@ -1018,9 +1064,9 @@ struct IMGUI_API ImGuiInputTextState bool CursorFollow; // set when we want scrolling to follow the current cursor position (not always!) bool SelectedAllMouseLock; // after a double-click to select all, we ignore further mouse drags to update selection bool Edited; // edited this frame - ImGuiInputTextFlags Flags; // copy of InputText() flags + ImGuiInputTextFlags Flags; // copy of InputText() flags. may be used to check if e.g. ImGuiInputTextFlags_Password is set. - ImGuiInputTextState() { memset(this, 0, sizeof(*this)); } + ImGuiInputTextState(ImGuiContext* ctx) { memset(this, 0, sizeof(*this)); Ctx = ctx;} void ClearText() { CurLenW = CurLenA = 0; TextW[0] = 0; TextA[0] = 0; CursorClamp(); } void ClearFreeMemory() { TextW.clear(); TextA.clear(); InitialTextA.clear(); } int GetUndoAvailCount() const { return Stb.undostate.undo_point; } @@ -1043,7 +1089,7 @@ struct ImGuiPopupData { ImGuiID PopupId; // Set on OpenPopup() ImGuiWindow* Window; // Resolved on BeginPopup() - may stay unresolved if user never calls OpenPopup() - ImGuiWindow* SourceWindow; // Set on OpenPopup() copy of NavWindow at the time of opening the popup + ImGuiWindow* BackupNavWindow;// Set on OpenPopup(), a NavWindow that will be restored on popup close int ParentNavLayer; // Resolved on BeginPopup(). Actually a ImGuiNavLayer type (declared down below), initialized to -1 which is not part of an enum, but serves well-enough as "not any of layers" value int OpenFrameCount; // Set on OpenPopup() ImGuiID OpenParentId; // Set on OpenPopup(), we need this to differentiate multiple menu sets from each others (e.g. inside menu bar vs loose menu items) @@ -1063,7 +1109,7 @@ enum ImGuiNextWindowDataFlags_ ImGuiNextWindowDataFlags_HasSizeConstraint = 1 << 4, ImGuiNextWindowDataFlags_HasFocus = 1 << 5, ImGuiNextWindowDataFlags_HasBgAlpha = 1 << 6, - ImGuiNextWindowDataFlags_HasScroll = 1 << 7 + ImGuiNextWindowDataFlags_HasScroll = 1 << 7, }; // Storage for SetNexWindow** functions @@ -1093,7 +1139,7 @@ enum ImGuiNextItemDataFlags_ { ImGuiNextItemDataFlags_None = 0, ImGuiNextItemDataFlags_HasWidth = 1 << 0, - ImGuiNextItemDataFlags_HasOpen = 1 << 1 + ImGuiNextItemDataFlags_HasOpen = 1 << 1, }; struct ImGuiNextItemData @@ -1168,13 +1214,27 @@ struct ImGuiPtrOrIndex typedef ImBitArray ImBitArrayForNamedKeys; -enum ImGuiKeyPrivate_ -{ - ImGuiKey_LegacyNativeKey_BEGIN = 0, - ImGuiKey_LegacyNativeKey_END = 512, - ImGuiKey_Gamepad_BEGIN = ImGuiKey_GamepadStart, - ImGuiKey_Gamepad_END = ImGuiKey_GamepadRStickRight + 1 -}; +// [Internal] Key ranges +#define ImGuiKey_LegacyNativeKey_BEGIN 0 +#define ImGuiKey_LegacyNativeKey_END 512 +#define ImGuiKey_Keyboard_BEGIN (ImGuiKey_NamedKey_BEGIN) +#define ImGuiKey_Keyboard_END (ImGuiKey_GamepadStart) +#define ImGuiKey_Gamepad_BEGIN (ImGuiKey_GamepadStart) +#define ImGuiKey_Gamepad_END (ImGuiKey_GamepadRStickDown + 1) +#define ImGuiKey_Mouse_BEGIN (ImGuiKey_MouseLeft) +#define ImGuiKey_Mouse_END (ImGuiKey_MouseWheelY + 1) +#define ImGuiKey_Aliases_BEGIN (ImGuiKey_Mouse_BEGIN) +#define ImGuiKey_Aliases_END (ImGuiKey_Mouse_END) + +// [Internal] Named shortcuts for Navigation +#define ImGuiKey_NavKeyboardTweakSlow ImGuiMod_Ctrl +#define ImGuiKey_NavKeyboardTweakFast ImGuiMod_Shift +#define ImGuiKey_NavGamepadTweakSlow ImGuiKey_GamepadL1 +#define ImGuiKey_NavGamepadTweakFast ImGuiKey_GamepadR1 +#define ImGuiKey_NavGamepadActivate ImGuiKey_GamepadFaceDown +#define ImGuiKey_NavGamepadCancel ImGuiKey_GamepadFaceRight +#define ImGuiKey_NavGamepadMenu ImGuiKey_GamepadFaceLeft +#define ImGuiKey_NavGamepadInput ImGuiKey_GamepadFaceUp enum ImGuiInputEventType { @@ -1226,15 +1286,94 @@ struct ImGuiInputEvent ImGuiInputEvent() { memset(this, 0, sizeof(*this)); } }; -// FIXME-NAV: Clarify/expose various repeat delay/rate -enum ImGuiNavReadMode +// Input function taking an 'ImGuiID owner_id' argument defaults to (ImGuiKeyOwner_Any == 0) aka don't test ownership, which matches legacy behavior. +#define ImGuiKeyOwner_Any ((ImGuiID)0) // Accept key that have an owner, UNLESS a call to SetKeyOwner() explicitly used ImGuiInputFlags_LockThisFrame or ImGuiInputFlags_LockUntilRelease. +#define ImGuiKeyOwner_None ((ImGuiID)-1) // Require key to have no owner. + +typedef ImS16 ImGuiKeyRoutingIndex; + +// Routing table entry (sizeof() == 16 bytes) +struct ImGuiKeyRoutingData { - ImGuiNavReadMode_Down, - ImGuiNavReadMode_Pressed, - ImGuiNavReadMode_Released, - ImGuiNavReadMode_Repeat, - ImGuiNavReadMode_RepeatSlow, - ImGuiNavReadMode_RepeatFast + ImGuiKeyRoutingIndex NextEntryIndex; + ImU16 Mods; // Technically we'd only need 4-bits but for simplify we store ImGuiMod_ values which need 16-bits. ImGuiMod_Shortcut is already translated to Ctrl/Super. + ImU8 RoutingNextScore; // Lower is better (0: perfect score) + ImGuiID RoutingCurr; + ImGuiID RoutingNext; + + ImGuiKeyRoutingData() { NextEntryIndex = -1; Mods = 0; RoutingNextScore = 255; RoutingCurr = RoutingNext = ImGuiKeyOwner_None; } +}; + +// Routing table: maintain a desired owner for each possible key-chord (key + mods), and setup owner in NewFrame() when mods are matching. +// Stored in main context (1 instance) +struct ImGuiKeyRoutingTable +{ + ImGuiKeyRoutingIndex Index[ImGuiKey_NamedKey_COUNT]; // Index of first entry in Entries[] + ImVector Entries; + ImVector EntriesNext; // Double-buffer to avoid reallocation (could use a shared buffer) + + ImGuiKeyRoutingTable() { Clear(); } + void Clear() { for (int n = 0; n < IM_ARRAYSIZE(Index); n++) Index[n] = -1; Entries.clear(); EntriesNext.clear(); } +}; + +// This extends ImGuiKeyData but only for named keys (legacy keys don't support the new features) +// Stored in main context (1 per named key). In the future it might be merged into ImGuiKeyData. +struct ImGuiKeyOwnerData +{ + ImGuiID OwnerCurr; + ImGuiID OwnerNext; + bool LockThisFrame; // Reading this key requires explicit owner id (until end of frame). Set by ImGuiInputFlags_LockThisFrame. + bool LockUntilRelease; // Reading this key requires explicit owner id (until key is released). Set by ImGuiInputFlags_LockUntilRelease. When this is true LockThisFrame is always true as well. + + ImGuiKeyOwnerData() { OwnerCurr = OwnerNext = ImGuiKeyOwner_None; LockThisFrame = LockUntilRelease = false; } +}; + +// Flags for extended versions of IsKeyPressed(), IsMouseClicked(), Shortcut(), SetKeyOwner(), SetItemKeyOwner() +// Don't mistake with ImGuiInputTextFlags! (for ImGui::InputText() function) +enum ImGuiInputFlags_ +{ + // Flags for IsKeyPressed(), IsMouseClicked(), Shortcut() + ImGuiInputFlags_None = 0, + ImGuiInputFlags_Repeat = 1 << 0, // Return true on successive repeats. Default for legacy IsKeyPressed(). NOT Default for legacy IsMouseClicked(). MUST BE == 1. + ImGuiInputFlags_RepeatRateDefault = 1 << 1, // Repeat rate: Regular (default) + ImGuiInputFlags_RepeatRateNavMove = 1 << 2, // Repeat rate: Fast + ImGuiInputFlags_RepeatRateNavTweak = 1 << 3, // Repeat rate: Faster + ImGuiInputFlags_RepeatRateMask_ = ImGuiInputFlags_RepeatRateDefault | ImGuiInputFlags_RepeatRateNavMove | ImGuiInputFlags_RepeatRateNavTweak, + + // Flags for SetItemKeyOwner() + ImGuiInputFlags_CondHovered = 1 << 4, // Only set if item is hovered (default to both) + ImGuiInputFlags_CondActive = 1 << 5, // Only set if item is active (default to both) + ImGuiInputFlags_CondDefault_ = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive, + ImGuiInputFlags_CondMask_ = ImGuiInputFlags_CondHovered | ImGuiInputFlags_CondActive, + + // Flags for SetKeyOwner(), SetItemKeyOwner() + ImGuiInputFlags_LockThisFrame = 1 << 6, // Access to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared at end of frame. This is useful to make input-owner-aware code steal keys from non-input-owner-aware code. + ImGuiInputFlags_LockUntilRelease = 1 << 7, // Access to key data will require EXPLICIT owner ID (ImGuiKeyOwner_Any/0 will NOT accepted for polling). Cleared when the key is released or at end of each frame if key is released. This is useful to make input-owner-aware code steal keys from non-input-owner-aware code. + + // Routing policies for Shortcut() + low-level SetShortcutRouting() + // - The general idea is that several callers register interest in a shortcut, and only one owner gets it. + // - When a policy (other than _RouteAlways) is set, Shortcut() will register itself with SetShortcutRouting(), + // allowing the system to decide where to route the input among other route-aware calls. + // - Shortcut() uses ImGuiInputFlags_RouteFocused by default: meaning that a simple Shortcut() poll + // will register a route and only succeed when parent window is in the focus stack and if no-one + // with a higher priority is claiming the shortcut. + // - Using ImGuiInputFlags_RouteAlways is roughly equivalent to doing e.g. IsKeyPressed(key) + testing mods. + // - Priorities: GlobalHigh > Focused (when owner is active item) > Global > Focused (when focused window) > GlobalLow. + // - Can select only 1 policy among all available. + ImGuiInputFlags_RouteFocused = 1 << 8, // (Default) Register focused route: Accept inputs if window is in focus stack. Deep-most focused window takes inputs. ActiveId takes inputs over deep-most focused window. + ImGuiInputFlags_RouteGlobalLow = 1 << 9, // Register route globally (lowest priority: unless a focused window or active item registered the route) -> recommended Global priority. + ImGuiInputFlags_RouteGlobal = 1 << 10, // Register route globally (medium priority: unless an active item registered the route, e.g. CTRL+A registered by InputText). + ImGuiInputFlags_RouteGlobalHigh = 1 << 11, // Register route globally (highest priority: unlikely you need to use that: will interfere with every active items) + ImGuiInputFlags_RouteMask_ = ImGuiInputFlags_RouteFocused | ImGuiInputFlags_RouteGlobal | ImGuiInputFlags_RouteGlobalLow | ImGuiInputFlags_RouteGlobalHigh, // _Always not part of this! + ImGuiInputFlags_RouteAlways = 1 << 12, // Do not register route, poll keys directly. + ImGuiInputFlags_RouteUnlessBgFocused= 1 << 13, // Global routes will not be applied if underlying background/void is focused (== no Dear ImGui windows are focused). Useful for overlay applications. + ImGuiInputFlags_RouteExtraMask_ = ImGuiInputFlags_RouteAlways | ImGuiInputFlags_RouteUnlessBgFocused, + + // [Internal] Mask of which function support which flags + ImGuiInputFlags_SupportedByIsKeyPressed = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_, + ImGuiInputFlags_SupportedByShortcut = ImGuiInputFlags_Repeat | ImGuiInputFlags_RepeatRateMask_ | ImGuiInputFlags_RouteMask_ | ImGuiInputFlags_RouteExtraMask_, + ImGuiInputFlags_SupportedBySetKeyOwner = ImGuiInputFlags_LockThisFrame | ImGuiInputFlags_LockUntilRelease, + ImGuiInputFlags_SupportedBySetItemKeyOwner = ImGuiInputFlags_SupportedBySetKeyOwner | ImGuiInputFlags_CondMask_, }; //----------------------------------------------------------------------------- @@ -1275,7 +1414,7 @@ enum ImGuiActivateFlags_ ImGuiActivateFlags_None = 0, ImGuiActivateFlags_PreferInput = 1 << 0, // Favor activation that requires keyboard text input (e.g. for Slider/Drag). Default if keyboard is available. ImGuiActivateFlags_PreferTweak = 1 << 1, // Favor activation for tweaking with arrows or gamepad (e.g. for Slider/Drag). Default if keyboard is not available. - ImGuiActivateFlags_TryToPreserveState = 1 << 2 // Request widget to preserve state if it can (e.g. InputText will try to preserve cursor/selection) + ImGuiActivateFlags_TryToPreserveState = 1 << 2, // Request widget to preserve state if it can (e.g. InputText will try to preserve cursor/selection) }; // Early work-in-progress API for ScrollToItem() @@ -1290,7 +1429,7 @@ enum ImGuiScrollFlags_ ImGuiScrollFlags_AlwaysCenterY = 1 << 5, // Always center the result item on Y axis [default for Y axis for appearing window) ImGuiScrollFlags_NoScrollParent = 1 << 6, // Disable forwarding scrolling to parent window if required to keep item/rect visible (only scroll window the function was applied to). ImGuiScrollFlags_MaskX_ = ImGuiScrollFlags_KeepVisibleEdgeX | ImGuiScrollFlags_KeepVisibleCenterX | ImGuiScrollFlags_AlwaysCenterX, - ImGuiScrollFlags_MaskY_ = ImGuiScrollFlags_KeepVisibleEdgeY | ImGuiScrollFlags_KeepVisibleCenterY | ImGuiScrollFlags_AlwaysCenterY + ImGuiScrollFlags_MaskY_ = ImGuiScrollFlags_KeepVisibleEdgeY | ImGuiScrollFlags_KeepVisibleCenterY | ImGuiScrollFlags_AlwaysCenterY, }; enum ImGuiNavHighlightFlags_ @@ -1299,16 +1438,7 @@ enum ImGuiNavHighlightFlags_ ImGuiNavHighlightFlags_TypeDefault = 1 << 0, ImGuiNavHighlightFlags_TypeThin = 1 << 1, ImGuiNavHighlightFlags_AlwaysDraw = 1 << 2, // Draw rectangular highlight if (g.NavId == id) _even_ when using the mouse. - ImGuiNavHighlightFlags_NoRounding = 1 << 3 -}; - -enum ImGuiNavDirSourceFlags_ -{ - ImGuiNavDirSourceFlags_None = 0, - ImGuiNavDirSourceFlags_RawKeyboard = 1 << 0, // Raw keyboard (not pulled from nav), facilitate use of some functions before we can unify nav and keys - ImGuiNavDirSourceFlags_Keyboard = 1 << 1, - ImGuiNavDirSourceFlags_PadDPad = 1 << 2, - ImGuiNavDirSourceFlags_PadLStick = 1 << 3 + ImGuiNavHighlightFlags_NoRounding = 1 << 3, }; enum ImGuiNavMoveFlags_ @@ -1326,13 +1456,13 @@ enum ImGuiNavMoveFlags_ ImGuiNavMoveFlags_FocusApi = 1 << 9, ImGuiNavMoveFlags_Tabbing = 1 << 10, // == Focus + Activate if item is Inputable + DontChangeNavHighlight ImGuiNavMoveFlags_Activate = 1 << 11, - ImGuiNavMoveFlags_DontSetNavHighlight = 1 << 12 // Do not alter the visible state of keyboard vs mouse nav highlight + ImGuiNavMoveFlags_DontSetNavHighlight = 1 << 12, // Do not alter the visible state of keyboard vs mouse nav highlight }; enum ImGuiNavLayer { ImGuiNavLayer_Main = 0, // Main scrolling layer - ImGuiNavLayer_Menu = 1, // Menu layer (access with Alt/ImGuiNavInput_Menu) + ImGuiNavLayer_Menu = 1, // Menu layer (access with Alt) ImGuiNavLayer_COUNT }; @@ -1363,24 +1493,24 @@ enum ImGuiOldColumnFlags_ ImGuiOldColumnFlags_NoResize = 1 << 1, // Disable resizing columns when clicking on the dividers ImGuiOldColumnFlags_NoPreserveWidths = 1 << 2, // Disable column width preservation when adjusting columns ImGuiOldColumnFlags_NoForceWithinWindow = 1 << 3, // Disable forcing columns to fit within window - ImGuiOldColumnFlags_GrowParentContentsSize = 1 << 4 // (WIP) Restore pre-1.51 behavior of extending the parent window contents size but _without affecting the columns width at all_. Will eventually remove. + ImGuiOldColumnFlags_GrowParentContentsSize = 1 << 4, // (WIP) Restore pre-1.51 behavior of extending the parent window contents size but _without affecting the columns width at all_. Will eventually remove. // Obsolete names (will be removed) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - , ImGuiColumnsFlags_None = ImGuiOldColumnFlags_None, + ImGuiColumnsFlags_None = ImGuiOldColumnFlags_None, ImGuiColumnsFlags_NoBorder = ImGuiOldColumnFlags_NoBorder, ImGuiColumnsFlags_NoResize = ImGuiOldColumnFlags_NoResize, ImGuiColumnsFlags_NoPreserveWidths = ImGuiOldColumnFlags_NoPreserveWidths, ImGuiColumnsFlags_NoForceWithinWindow = ImGuiOldColumnFlags_NoForceWithinWindow, - ImGuiColumnsFlags_GrowParentContentsSize = ImGuiOldColumnFlags_GrowParentContentsSize + ImGuiColumnsFlags_GrowParentContentsSize = ImGuiOldColumnFlags_GrowParentContentsSize, #endif }; struct ImGuiOldColumnData { - float OffsetNorm; // Column start offset, normalized 0.0 (far left) -> 1.0 (far right) + float OffsetNorm; // Column start offset, normalized 0.0 (far left) -> 1.0 (far right) float OffsetNormBeforeResize; - ImGuiOldColumnFlags Flags; // Not exposed + ImGuiOldColumnFlags Flags; // Not exposed ImRect ClipRect; ImGuiOldColumnData() { memset(this, 0, sizeof(*this)); } @@ -1489,6 +1619,30 @@ struct ImGuiSettingsHandler ImGuiSettingsHandler() { memset(this, 0, sizeof(*this)); } }; +//----------------------------------------------------------------------------- +// [SECTION] Localization support +//----------------------------------------------------------------------------- + +// This is experimental and not officially supported, it'll probably fall short of features, if/when it does we may backtrack. +enum ImGuiLocKey : int +{ + ImGuiLocKey_TableSizeOne, + ImGuiLocKey_TableSizeAllFit, + ImGuiLocKey_TableSizeAllDefault, + ImGuiLocKey_TableResetOrder, + ImGuiLocKey_WindowingMainMenuBar, + ImGuiLocKey_WindowingPopup, + ImGuiLocKey_WindowingUntitled, + ImGuiLocKey_COUNT +}; + +struct ImGuiLocEntry +{ + ImGuiLocKey Key; + const char* Text; +}; + + //----------------------------------------------------------------------------- // [SECTION] Metrics, Debug Tools //----------------------------------------------------------------------------- @@ -1501,9 +1655,10 @@ enum ImGuiDebugLogFlags_ ImGuiDebugLogFlags_EventFocus = 1 << 1, ImGuiDebugLogFlags_EventPopup = 1 << 2, ImGuiDebugLogFlags_EventNav = 1 << 3, - ImGuiDebugLogFlags_EventIO = 1 << 4, - ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventIO, - ImGuiDebugLogFlags_OutputToTTY = 1 << 10 // Also send output to TTY + ImGuiDebugLogFlags_EventClipper = 1 << 4, + ImGuiDebugLogFlags_EventIO = 1 << 5, + ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventIO, + ImGuiDebugLogFlags_OutputToTTY = 1 << 10, // Also send output to TTY }; struct ImGuiMetricsConfig @@ -1610,15 +1765,16 @@ struct ImGuiContext ImGuiWindow* MovingWindow; // Track the window we clicked on (in order to preserve focus). The actual window that is moved is generally MovingWindow->RootWindow. ImGuiWindow* WheelingWindow; // Track the window we started mouse-wheeling on. Until a timer elapse or mouse has moved, generally keep scrolling the same window even if during the course of scrolling the mouse ends up hovering a child window. ImVec2 WheelingWindowRefMousePos; - float WheelingWindowTimer; + int WheelingWindowStartFrame; // This may be set one frame before WheelingWindow is != NULL + float WheelingWindowReleaseTimer; + ImVec2 WheelingWindowWheelRemainder; + ImVec2 WheelingAxisAvg; // Item/widgets state and tracking information ImGuiID DebugHookIdInfo; // Will call core hooks: DebugHookIdInfo() from GetID functions, used by Stack Tool [next HoveredId/ActiveId to not pull in an extra cache-line] ImGuiID HoveredId; // Hovered widget, filled during the frame ImGuiID HoveredIdPreviousFrame; bool HoveredIdAllowOverlap; - bool HoveredIdUsingMouseWheel; // Hovered widget will use mouse wheel. Blocks scrolling the underlying window. - bool HoveredIdPreviousFrameUsingMouseWheel; bool HoveredIdDisabled; // At least one widget passed the rect test, but has been discarded by disabled flag or popup inhibit. May be true even if HoveredId == 0. float HoveredIdTimer; // Measure contiguous hovering time float HoveredIdNotActiveTimer; // Measure contiguous hovering time where the item has not been active @@ -1642,14 +1798,22 @@ struct ImGuiContext ImGuiID LastActiveId; // Store the last non-zero ActiveId, useful for animation. float LastActiveIdTimer; // Store the last non-zero ActiveId timer since the beginning of activation, useful for animation. - // Input Ownership - bool ActiveIdUsingMouseWheel; // Active widget will want to read mouse wheel. Blocks scrolling the underlying window. + // [EXPERIMENTAL] Key/Input Ownership + Shortcut Routing system + // - The idea is that instead of "eating" a given key, we can link to an owner. + // - Input query can then read input by specifying ImGuiKeyOwner_Any (== 0), ImGuiKeyOwner_None (== -1) or a custom ID. + // - Routing is requested ahead of time for a given chord (Key + Mods) and granted in NewFrame(). + ImGuiKeyOwnerData KeysOwnerData[ImGuiKey_NamedKey_COUNT]; + ImGuiKeyRoutingTable KeysRoutingTable; ImU32 ActiveIdUsingNavDirMask; // Active widget will want to read those nav move requests (e.g. can activate a button and move away from it) - ImU32 ActiveIdUsingNavInputMask; // Active widget will want to read those nav inputs. - ImBitArrayForNamedKeys ActiveIdUsingKeyInputMask; // Active widget will want to read those key inputs. When we grow the ImGuiKey enum we'll need to either to order the enum to make useful keys come first, either redesign this into e.g. a small array. + bool ActiveIdUsingAllKeyboardKeys; // Active widget will want to read all keyboard keys inputs. (FIXME: This is a shortcut for not taking ownership of 100+ keys but perhaps best to not have the inconsistency) +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + ImU32 ActiveIdUsingNavInputMask; // If you used this. Since (IMGUI_VERSION_NUM >= 18804) : 'g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel);' becomes 'SetKeyOwner(ImGuiKey_Escape, g.ActiveId) and/or SetKeyOwner(ImGuiKey_NavGamepadCancel, g.ActiveId);' +#endif // Next window/item data - ImGuiItemFlags CurrentItemFlags; // == g.ItemFlagsStack.back() + ImGuiID CurrentFocusScopeId; // == g.FocusScopeStack.back() + ImGuiItemFlags CurrentItemFlags; // == g.ItemFlagsStack.back() + ImGuiID DebugLocateId; // Storage for DebugLocateItemOnHover() feature: this is read by ItemAdd() so we keep it in a hot/cached location ImGuiNextItemData NextItemData; // Storage for SetNextItem** functions ImGuiLastItemData LastItemData; // Storage for last submitted item (setup by ItemAdd) ImGuiNextWindowData NextWindowData; // Storage for SetNextWindow** functions @@ -1658,7 +1822,7 @@ struct ImGuiContext ImVector ColorStack; // Stack for PushStyleColor()/PopStyleColor() - inherited by Begin() ImVector StyleVarStack; // Stack for PushStyleVar()/PopStyleVar() - inherited by Begin() ImVector FontStack; // Stack for PushFont()/PopFont() - inherited by Begin() - ImVector FocusScopeStack; // Stack for PushFocusScope()/PopFocusScope() - not inherited by Begin(), unless child window + ImVector FocusScopeStack; // Stack for PushFocusScope()/PopFocusScope() - inherited by BeginChild(), pushed into by Begin() ImVectorItemFlagsStack; // Stack for PushItemFlag()/PopItemFlag() - inherited by Begin() ImVectorGroupStack; // Stack for BeginGroup()/EndGroup() - not inherited by Begin() ImVectorOpenPopupStack; // Which popups are open (persistent) @@ -1672,14 +1836,14 @@ struct ImGuiContext ImGuiWindow* NavWindow; // Focused window for navigation. Could be called 'FocusedWindow' ImGuiID NavId; // Focused item for navigation ImGuiID NavFocusScopeId; // Identify a selection scope (selection code often wants to "clear other items" when landing on an item of the selection set) - ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0, also set when calling ActivateItem() - ImGuiID NavActivateDownId; // ~~ IsNavInputDown(ImGuiNavInput_Activate) ? NavId : 0 - ImGuiID NavActivatePressedId; // ~~ IsNavInputPressed(ImGuiNavInput_Activate) ? NavId : 0 - ImGuiID NavActivateInputId; // ~~ IsNavInputPressed(ImGuiNavInput_Input) ? NavId : 0; ImGuiActivateFlags_PreferInput will be set and NavActivateId will be 0. + ImGuiID NavActivateId; // ~~ (g.ActiveId == 0) && (IsKeyPressed(ImGuiKey_Space) || IsKeyPressed(ImGuiKey_NavGamepadActivate)) ? NavId : 0, also set when calling ActivateItem() + ImGuiID NavActivateDownId; // ~~ IsKeyDown(ImGuiKey_Space) || IsKeyDown(ImGuiKey_NavGamepadActivate) ? NavId : 0 + ImGuiID NavActivatePressedId; // ~~ IsKeyPressed(ImGuiKey_Space) || IsKeyPressed(ImGuiKey_NavGamepadActivate) ? NavId : 0 (no repeat) + ImGuiID NavActivateInputId; // ~~ IsKeyPressed(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_NavGamepadInput) ? NavId : 0; ImGuiActivateFlags_PreferInput will be set and NavActivateId will be 0. ImGuiActivateFlags NavActivateFlags; ImGuiID NavJustMovedToId; // Just navigated to this id (result of a successfully MoveRequest). ImGuiID NavJustMovedToFocusScopeId; // Just navigated to this focus scope id (result of a successfully MoveRequest). - ImGuiModFlags NavJustMovedToKeyMods; + ImGuiKeyChord NavJustMovedToKeyMods; ImGuiID NavNextActivateId; // Set by ActivateItem(), queued until next frame. ImGuiActivateFlags NavNextActivateFlags; ImGuiInputSource NavInputSource; // Keyboard or Gamepad mode? THIS WILL ONLY BE None or NavGamepad or NavKeyboard. @@ -1700,7 +1864,7 @@ struct ImGuiContext bool NavMoveForwardToNextFrame; ImGuiNavMoveFlags NavMoveFlags; ImGuiScrollFlags NavMoveScrollFlags; - ImGuiModFlags NavMoveKeyMods; + ImGuiKeyChord NavMoveKeyMods; ImGuiDir NavMoveDir; // Direction of the move request (left/right/up/down) ImGuiDir NavMoveDirForDebug; ImGuiDir NavMoveClipDir; // FIXME-NAV: Describe the purpose of this better. Might want to rename? @@ -1715,12 +1879,16 @@ struct ImGuiContext ImGuiNavItemData NavTabbingResultFirst; // First tabbing request candidate within NavWindow and flattened hierarchy // Navigation: Windowing (CTRL+TAB for list, or Menu button + keys or directional pads to move/resize) + ImGuiKeyChord ConfigNavWindowingKeyNext; // = ImGuiMod_Ctrl | ImGuiKey_Tab, for reconfiguration (see #4828) + ImGuiKeyChord ConfigNavWindowingKeyPrev; // = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab ImGuiWindow* NavWindowingTarget; // Target window when doing CTRL+Tab (or Pad Menu + FocusPrev/Next), this window is temporarily displayed top-most! ImGuiWindow* NavWindowingTargetAnim; // Record of last valid NavWindowingTarget until DimBgRatio and NavWindowingHighlightAlpha becomes 0.0f, so the fade-out can stay on it. ImGuiWindow* NavWindowingListWindow; // Internal window actually listing the CTRL+Tab contents float NavWindowingTimer; float NavWindowingHighlightAlpha; bool NavWindowingToggleLayer; + ImVec2 NavWindowingAccumDeltaPos; + ImVec2 NavWindowingAccumDeltaSize; // Render float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) @@ -1763,6 +1931,12 @@ struct ImGuiContext ImVector CurrentTabBarStack; ImVector ShrinkWidthBuffer; + // Hover Delay system + ImGuiID HoverDelayId; + ImGuiID HoverDelayIdPreviousFrame; + float HoverDelayTimer; // Currently used IsItemHovered(), generally inferred from g.HoveredIdTimer but kept uncleared until clear timer elapse. + float HoverDelayClearTimer; // Currently used IsItemHovered(): grace time before g.TooltipHoverTimer gets cleared. + // Widget state ImVec2 MouseLastValidPos; ImGuiInputTextState InputTextState; @@ -1784,7 +1958,6 @@ struct ImGuiContext float DisabledAlphaBackup; // Backup for style.Alpha for BeginDisabled() short DisabledStackSize; short TooltipOverrideCount; - float TooltipSlowDelay; // Time before slow tooltips appears (FIXME: This is temporary until we merge in tooltip timer+priority work) ImVector ClipboardHandlerData; // If no custom clipboard handler is defined ImVector MenusIdSubmittedThisFrame; // A list of menu IDs that were rendered at least once @@ -1803,6 +1976,9 @@ struct ImGuiContext ImVector Hooks; // Hooks for extensions (e.g. test engine) ImGuiID HookIdNext; // Next available HookId + // Localization + const char* LocalizationTable[ImGuiLocKey_COUNT]; + // Capture/Logging bool LogEnabled; // Currently capturing ImGuiLogType LogType; // Capture target @@ -1819,13 +1995,16 @@ struct ImGuiContext // Debug Tools ImGuiDebugLogFlags DebugLogFlags; ImGuiTextBuffer DebugLogBuf; + ImGuiTextIndex DebugLogIndex; + ImU8 DebugLocateFrames; // For DebugLocateItemOnHover(). This is used together with DebugLocateId which is in a hot/cached spot above. bool DebugItemPickerActive; // Item picker is active (started with DebugStartItemPicker()) + ImU8 DebugItemPickerMouseButton; ImGuiID DebugItemPickerBreakId; // Will call IM_DEBUG_BREAK() when encountering this ID ImGuiMetricsConfig DebugMetricsConfig; ImGuiStackTool DebugStackTool; // Misc - float FramerateSecPerFrame[120]; // Calculate estimate of framerate for user over the last 2 seconds. + float FramerateSecPerFrame[60]; // Calculate estimate of framerate for user over the last 60 frames.. int FramerateSecPerFrameIdx; int FramerateSecPerFrameCount; float FramerateSecPerFrameAccum; @@ -1835,6 +2014,7 @@ struct ImGuiContext ImVector TempBuffer; // Temporary text buffer ImGuiContext(ImFontAtlas* shared_font_atlas) + : InputTextState(this) { Initialized = false; FontAtlasOwnedByContext = shared_font_atlas ? false : true; @@ -1855,12 +2035,12 @@ struct ImGuiContext HoveredWindowUnderMovingWindow = NULL; MovingWindow = NULL; WheelingWindow = NULL; - WheelingWindowTimer = 0.0f; + WheelingWindowStartFrame = -1; + WheelingWindowReleaseTimer = 0.0f; DebugHookIdInfo = 0; HoveredId = HoveredIdPreviousFrame = 0; HoveredIdAllowOverlap = false; - HoveredIdUsingMouseWheel = HoveredIdPreviousFrameUsingMouseWheel = false; HoveredIdDisabled = false; HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f; ActiveId = 0; @@ -1883,11 +2063,13 @@ struct ImGuiContext LastActiveId = 0; LastActiveIdTimer = 0.0f; - ActiveIdUsingMouseWheel = false; ActiveIdUsingNavDirMask = 0x00; + ActiveIdUsingAllKeyboardKeys = false; +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO ActiveIdUsingNavInputMask = 0x00; - ActiveIdUsingKeyInputMask.ClearAllBits(); +#endif + CurrentFocusScopeId = 0; CurrentItemFlags = ImGuiItemFlags_None; BeginMenuCount = 0; @@ -1895,7 +2077,7 @@ struct ImGuiContext NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = NavActivateInputId = 0; NavJustMovedToId = NavJustMovedToFocusScopeId = NavNextActivateId = 0; NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None; - NavJustMovedToKeyMods = ImGuiModFlags_None; + NavJustMovedToKeyMods = ImGuiMod_None; NavInputSource = ImGuiInputSource_None; NavLayer = ImGuiNavLayer_Main; NavIdIsAlive = false; @@ -1911,12 +2093,14 @@ struct ImGuiContext NavMoveForwardToNextFrame = false; NavMoveFlags = ImGuiNavMoveFlags_None; NavMoveScrollFlags = ImGuiScrollFlags_None; - NavMoveKeyMods = ImGuiModFlags_None; + NavMoveKeyMods = ImGuiMod_None; NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None; NavScoringDebugCount = 0; NavTabbingDir = 0; NavTabbingCounter = 0; + ConfigNavWindowingKeyNext = ImGuiMod_Ctrl | ImGuiKey_Tab; + ConfigNavWindowingKeyPrev = ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab; NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL; NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f; NavWindowingToggleLayer = false; @@ -1942,6 +2126,9 @@ struct ImGuiContext TablesTempDataStacked = 0; CurrentTabBar = NULL; + HoverDelayId = HoverDelayIdPreviousFrame = 0; + HoverDelayTimer = HoverDelayClearTimer = 0.0f; + TempInputId = 0; ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_; ColorEditLastHue = ColorEditLastSat = 0.0f; @@ -1952,11 +2139,10 @@ struct ImGuiContext DragCurrentAccumDirty = false; DragCurrentAccum = 0.0f; DragSpeedDefaultRatio = 1.0f / 100.0f; + ScrollbarClickDeltaToGrabCenter = 0.0f; DisabledAlphaBackup = 0.0f; DisabledStackSize = 0; - ScrollbarClickDeltaToGrabCenter = 0.0f; TooltipOverrideCount = 0; - TooltipSlowDelay = 0.50f; PlatformImeData.InputPos = ImVec2(0.0f, 0.0f); PlatformImeDataPrev.InputPos = ImVec2(-1.0f, -1.0f); // Different to ensure initial submission @@ -1966,6 +2152,8 @@ struct ImGuiContext SettingsDirtyTimer = 0.0f; HookIdNext = 0; + memset(LocalizationTable, 0, sizeof(LocalizationTable)); + LogEnabled = false; LogType = ImGuiLogType_None; LogNextPrefix = LogNextSuffix = NULL; @@ -1976,7 +2164,10 @@ struct ImGuiContext LogDepthToExpand = LogDepthToExpandDefault = 2; DebugLogFlags = ImGuiDebugLogFlags_OutputToTTY; + DebugLocateId = 0; + DebugLocateFrames = 0; DebugItemPickerActive = false; + DebugItemPickerMouseButton = ImGuiMouseButton_Left; DebugItemPickerBreakId = 0; memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame)); @@ -2006,6 +2197,7 @@ struct IMGUI_API ImGuiWindowTempData float CurrLineTextBaseOffset; // Baseline offset (0.0f by default on a new line, generally == style.FramePadding.y when a framed item has been added). float PrevLineTextBaseOffset; bool IsSameLine; + bool IsSetPos; ImVec1 Indent; // Indentation / start position from left of window (increased by TreePush/TreePop, etc.) ImVec1 ColumnsOffset; // Offset to the current column (if ColumnsCurrent > 0). FIXME: This and the above should be a stack to allow use cases like Tree->Column->Tree. Need revamp columns API. ImVec1 GroupOffset; @@ -2015,7 +2207,6 @@ struct IMGUI_API ImGuiWindowTempData ImGuiNavLayer NavLayerCurrent; // Current layer, 0..31 (we currently only use 0..1) short NavLayersActiveMask; // Which layers have been written to (result from previous frame) short NavLayersActiveMaskNext;// Which layers have been written to (accumulator for current frame) - ImGuiID NavFocusScopeIdCurrent; // Current focus scope ID while appending bool NavHideHighlightOneFrame; bool NavHasScroll; // Set when scrolling can be used (ScrollMax > 0.0f) @@ -2056,6 +2247,9 @@ struct IMGUI_API ImGuiWindow ImVec2 WindowPadding; // Window padding at the time of Begin(). float WindowRounding; // Window rounding at the time of Begin(). May be clamped lower to avoid rendering artifacts with title bar, menu bar etc. float WindowBorderSize; // Window border size at the time of Begin(). + float DecoOuterSizeX1, DecoOuterSizeY1; // Left/Up offsets. Sum of non-scrolling outer decorations (X1 generally == 0.0f. Y1 generally = TitleBarHeight + MenuBarHeight). Locked during Begin(). + float DecoOuterSizeX2, DecoOuterSizeY2; // Right/Down offsets (X2 generally == ScrollbarSize.x, Y2 == ScrollbarSizes.y). + float DecoInnerSizeX1, DecoInnerSizeY1; // Applied AFTER/OVER InnerRect. Specialized for Tables as they use specialized form of clipping and frozen rows/columns are inside InnerRect (and not part of regular decoration sizes). int NameBufLen; // Size of buffer storing Name. May be larger than strlen(Name)! ImGuiID MoveId; // == window->GetID("#MOVE") ImGuiID ChildId; // ID of corresponding item in parent window (for navigation to return from child window to parent window) @@ -2079,6 +2273,7 @@ struct IMGUI_API ImGuiWindow bool HasCloseButton; // Set when the window has a close button (p_open != NULL) signed char ResizeBorderHeld; // Current border being held for resize (-1: none, otherwise 0-3) short BeginCount; // Number of Begin() during the current frame (generally 0 or 1, 1+ if appending via multiple Begin/End pairs) + short BeginCountPreviousFrame; // Number of Begin() during the previous frame short BeginOrderWithinParent; // Begin() order within immediate parent window, if we are a child window. Otherwise 0. short BeginOrderWithinContext; // Begin() order within entire imgui context. This is mostly used for debugging submission order related issues. short FocusOrder; // Order within WindowsFocusOrder[], altered when windows are focused. @@ -2132,6 +2327,7 @@ struct IMGUI_API ImGuiWindow ImGuiWindow* NavLastChildNavWindow; // When going to the menu bar, we remember the child window we came from. (This could probably be made implicit if we kept g.Windows sorted by last focused including child window.) ImGuiID NavLastIds[ImGuiNavLayer_COUNT]; // Last known NavId for this window, per layer (0/1) ImRect NavRectRel[ImGuiNavLayer_COUNT]; // Reference rectangle, in window relative space + ImGuiID NavRootFocusScopeId; // Focus Scope ID at the time of Begin() int MemoryDrawListIdxCapacity; // Backup of last idx/vtx count, so when waking up the window we can preallocate and avoid iterative alloc/copy int MemoryDrawListVtxCapacity; @@ -2146,7 +2342,7 @@ public: ImGuiID GetID(int n); ImGuiID GetIDFromRectangle(const ImRect& r_abs); - // We don't use g.FontSize because the window may be != g.CurrentWidow. + // We don't use g.FontSize because the window may be != g.CurrentWindow. ImRect Rect() const { return ImRect(Pos.x, Pos.y, Pos.x + Size.x, Pos.y + Size.y); } float CalcFontSize() const { ImGuiContext& g = *GImGui; float scale = g.FontBaseSize * FontWindowScale; if (ParentWindow) scale *= ParentWindow->FontWindowScale; return scale; } float TitleBarHeight() const { ImGuiContext& g = *GImGui; return (Flags & ImGuiWindowFlags_NoTitleBar) ? 0.0f : CalcFontSize() + g.Style.FramePadding.y * 2.0f; } @@ -2164,7 +2360,7 @@ enum ImGuiTabBarFlagsPrivate_ { ImGuiTabBarFlags_DockNode = 1 << 20, // Part of a dock node [we don't use this in the master branch but it facilitate branch syncing to keep this around] ImGuiTabBarFlags_IsFocused = 1 << 21, - ImGuiTabBarFlags_SaveSettings = 1 << 22 // FIXME: Settings are handled by the docking system, this only request the tab bar to mark settings dirty when reordering tabs + ImGuiTabBarFlags_SaveSettings = 1 << 22, // FIXME: Settings are handled by the docking system, this only request the tab bar to mark settings dirty when reordering tabs }; // Extend ImGuiTabItemFlags_ @@ -2172,7 +2368,7 @@ enum ImGuiTabItemFlagsPrivate_ { ImGuiTabItemFlags_SectionMask_ = ImGuiTabItemFlags_Leading | ImGuiTabItemFlags_Trailing, ImGuiTabItemFlags_NoCloseButton = 1 << 20, // Track whether p_open was set or not (we'll need this info on the next frame to recompute ContentWidth during layout) - ImGuiTabItemFlags_Button = 1 << 21 // Used by TabItemButton, change the tab item behavior to mimic a button + ImGuiTabItemFlags_Button = 1 << 21, // Used by TabItemButton, change the tab item behavior to mimic a button }; // Storage for one active tab item (sizeof() 40 bytes) @@ -2191,7 +2387,7 @@ struct ImGuiTabItem ImS16 IndexDuringLayout; // Index only used during TabBarLayout() bool WantClose; // Marked as closed by SetTabItemClosed() - ImGuiTabItem() { memset(this, 0, sizeof(*this)); LastFrameVisible = LastFrameSelected = -1; NameOffset = -1; BeginOrder = IndexDuringLayout = -1; } + ImGuiTabItem() { memset(this, 0, sizeof(*this)); LastFrameVisible = LastFrameSelected = -1; RequestedWidth = -1.0f; NameOffset = -1; BeginOrder = IndexDuringLayout = -1; } }; // Storage for a tab bar (sizeof() 152 bytes) @@ -2322,13 +2518,14 @@ struct ImGuiTableCellData // Per-instance data that needs preserving across frames (seemingly most others do not need to be preserved aside from debug needs, does that needs they could be moved to ImGuiTableTempData ?) struct ImGuiTableInstanceData { - float LastOuterHeight; // Outer height from last frame // FIXME: multi-instance issue (#3955) - float LastFirstRowHeight; // Height of first row from last frame // FIXME: possible multi-instance issue? + float LastOuterHeight; // Outer height from last frame + float LastFirstRowHeight; // Height of first row from last frame (FIXME: this is used as "header height" and may be reworked) + float LastFrozenHeight; // Height of frozen section from last frame - ImGuiTableInstanceData() { LastOuterHeight = LastFirstRowHeight = 0.0f; } + ImGuiTableInstanceData() { LastOuterHeight = LastFirstRowHeight = LastFrozenHeight = 0.0f; } }; -// FIXME-TABLE: more transient data could be stored in a per-stacked table structure: DrawSplitter, SortSpecs, incoming RowData +// FIXME-TABLE: more transient data could be stored in a stacked ImGuiTableTempData: e.g. SortSpecs, incoming RowData struct IMGUI_API ImGuiTable { ImGuiID ID; @@ -2433,6 +2630,8 @@ struct IMGUI_API ImGuiTable bool IsResetDisplayOrderRequest; bool IsUnfrozenRows; // Set when we got past the frozen row. bool IsDefaultSizingPolicy; // Set if user didn't explicitly set a sizing policy in BeginTable() + bool HasScrollbarYCurr; // Whether ANY instance of this table had a vertical scrollbar during the current frame. + bool HasScrollbarYPrev; // Whether ANY instance of this table had a vertical scrollbar during the previous. bool MemoryCompacted; bool HostSkipItems; // Backup of InnerWindow->SkipItem at the end of BeginTable(), because we will overwrite InnerWindow->SkipItem on a per-column basis @@ -2577,8 +2776,11 @@ namespace ImGui IMGUI_API void RemoveSettingsHandler(const char* type_name); IMGUI_API ImGuiSettingsHandler* FindSettingsHandler(const char* type_name); + // Localization + IMGUI_API void LocalizeRegisterEntries(const ImGuiLocEntry* entries, int count); + inline const char* LocalizeGetMsg(ImGuiLocKey key) { ImGuiContext& g = *GImGui; const char* msg = g.LocalizationTable[key]; return msg ? msg : "*Missing Text*"; } + // Scrolling - IMGUI_API void SetNextWindowScroll(const ImVec2& scroll); // Use -1.0f on one axis to leave as-is IMGUI_API void SetScrollX(ImGuiWindow* window, float scroll_x); IMGUI_API void SetScrollY(ImGuiWindow* window, float scroll_y); IMGUI_API void SetScrollFromPosX(ImGuiWindow* window, float local_x, float center_x_ratio); @@ -2593,7 +2795,6 @@ namespace ImGui //#endif // Basic Accessors - inline ImGuiID GetItemID() { ImGuiContext& g = *GImGui; return g.LastItemData.ID; } // Get ID of last item (~~ often same ImGui::GetID(label) beforehand) inline ImGuiItemStatusFlags GetItemStatusFlags(){ ImGuiContext& g = *GImGui; return g.LastItemData.StatusFlags; } inline ImGuiItemFlags GetItemFlags() { ImGuiContext& g = *GImGui; return g.LastItemData.InFlags; } inline ImGuiID GetActiveID() { ImGuiContext& g = *GImGui; return g.ActiveId; } @@ -2622,21 +2823,10 @@ namespace ImGui IMGUI_API ImVec2 GetContentRegionMaxAbs(); IMGUI_API void ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_excess); - // Parameter stacks + // Parameter stacks (shared) IMGUI_API void PushItemFlag(ImGuiItemFlags option, bool enabled); IMGUI_API void PopItemFlag(); -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - // Currently refactoring focus/nav/tabbing system - // If you have old/custom copy-and-pasted widgets that used FocusableItemRegister(): - // (Old) IMGUI_VERSION_NUM < 18209: using 'ItemAdd(....)' and 'bool tab_focused = FocusableItemRegister(...)' - // (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool tab_focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0' - // (New) IMGUI_VERSION_NUM >= 18413: using 'ItemAdd(..., ImGuiItemFlags_Inputable)' and 'bool tab_focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_FocusedTabbing) != 0 || g.NavActivateInputId == id' (WIP) - // Widget code are simplified as there's no need to call FocusableItemUnregister() while managing the transition from regular widget to TempInputText() - inline bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id) { IM_ASSERT(0); IM_UNUSED(window); IM_UNUSED(id); return false; } // -> pass ImGuiItemAddFlags_Inputable flag to ItemAdd() - inline void FocusableItemUnregister(ImGuiWindow* window) { IM_ASSERT(0); IM_UNUSED(window); } // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem -#endif - // Logging/Capture IMGUI_API void LogBegin(ImGuiLogType type, int auto_open_depth); // -> BeginCapture() when we design v2 api, for now stay under the radar by using the old name. IMGUI_API void LogToBuffer(int auto_open_depth = -1); // Start logging/capturing to internal buffer @@ -2678,47 +2868,105 @@ namespace ImGui IMGUI_API void NavMoveRequestCancel(); IMGUI_API void NavMoveRequestApplyResult(); IMGUI_API void NavMoveRequestTryWrapping(ImGuiWindow* window, ImGuiNavMoveFlags move_flags); - IMGUI_API const char* GetNavInputName(ImGuiNavInput n); - IMGUI_API float GetNavInputAmount(ImGuiNavInput n, ImGuiNavReadMode mode); - IMGUI_API ImVec2 GetNavInputAmount2d(ImGuiNavDirSourceFlags dir_sources, ImGuiNavReadMode mode, float slow_factor = 0.0f, float fast_factor = 0.0f); - IMGUI_API int CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate); IMGUI_API void ActivateItem(ImGuiID id); // Remotely activate a button, checkbox, tree node etc. given its unique ID. activation is queued and processed on the next frame when the item is encountered again. IMGUI_API void SetNavWindow(ImGuiWindow* window); IMGUI_API void SetNavID(ImGuiID id, ImGuiNavLayer nav_layer, ImGuiID focus_scope_id, const ImRect& rect_rel); - // Focus Scope (WIP) - // This is generally used to identify a selection set (multiple of which may be in the same window), as selection - // patterns generally need to react (e.g. clear selection) when landing on an item of the set. - IMGUI_API void PushFocusScope(ImGuiID id); - IMGUI_API void PopFocusScope(); - inline ImGuiID GetFocusedFocusScope() { ImGuiContext& g = *GImGui; return g.NavFocusScopeId; } // Focus scope which is actually active - inline ImGuiID GetFocusScope() { ImGuiContext& g = *GImGui; return g.CurrentWindow->DC.NavFocusScopeIdCurrent; } // Focus scope we are outputting into, set by PushFocusScope() - // Inputs // FIXME: Eventually we should aim to move e.g. IsActiveIdUsingKey() into IsKeyXXX functions. inline bool IsNamedKey(ImGuiKey key) { return key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END; } + inline bool IsNamedKeyOrModKey(ImGuiKey key) { return (key >= ImGuiKey_NamedKey_BEGIN && key < ImGuiKey_NamedKey_END) || key == ImGuiMod_Ctrl || key == ImGuiMod_Shift || key == ImGuiMod_Alt || key == ImGuiMod_Super || key == ImGuiMod_Shortcut; } inline bool IsLegacyKey(ImGuiKey key) { return key >= ImGuiKey_LegacyNativeKey_BEGIN && key < ImGuiKey_LegacyNativeKey_END; } + inline bool IsKeyboardKey(ImGuiKey key) { return key >= ImGuiKey_Keyboard_BEGIN && key < ImGuiKey_Keyboard_END; } inline bool IsGamepadKey(ImGuiKey key) { return key >= ImGuiKey_Gamepad_BEGIN && key < ImGuiKey_Gamepad_END; } + inline bool IsMouseKey(ImGuiKey key) { return key >= ImGuiKey_Mouse_BEGIN && key < ImGuiKey_Mouse_END; } + inline bool IsAliasKey(ImGuiKey key) { return key >= ImGuiKey_Aliases_BEGIN && key < ImGuiKey_Aliases_END; } + inline ImGuiKeyChord ConvertShortcutMod(ImGuiKeyChord key_chord) { ImGuiContext& g = *GImGui; IM_ASSERT_PARANOID(key_chord & ImGuiMod_Shortcut); return (key_chord & ~ImGuiMod_Shortcut) | (g.IO.ConfigMacOSXBehaviors ? ImGuiMod_Super : ImGuiMod_Ctrl); } + inline ImGuiKey ConvertSingleModFlagToKey(ImGuiKey key) + { + ImGuiContext& g = *GImGui; + if (key == ImGuiMod_Ctrl) return ImGuiKey_ReservedForModCtrl; + if (key == ImGuiMod_Shift) return ImGuiKey_ReservedForModShift; + if (key == ImGuiMod_Alt) return ImGuiKey_ReservedForModAlt; + if (key == ImGuiMod_Super) return ImGuiKey_ReservedForModSuper; + if (key == ImGuiMod_Shortcut) return (g.IO.ConfigMacOSXBehaviors ? ImGuiKey_ReservedForModSuper : ImGuiKey_ReservedForModCtrl); + return key; + } + IMGUI_API ImGuiKeyData* GetKeyData(ImGuiKey key); - IMGUI_API void SetItemUsingMouseWheel(); - IMGUI_API void SetActiveIdUsingNavAndKeys(); - inline bool IsActiveIdUsingNavDir(ImGuiDir dir) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; } - inline bool IsActiveIdUsingNavInput(ImGuiNavInput input) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavInputMask & (1 << input)) != 0; } - inline bool IsActiveIdUsingKey(ImGuiKey key) { ImGuiContext& g = *GImGui; return g.ActiveIdUsingKeyInputMask[key]; } - inline void SetActiveIdUsingKey(ImGuiKey key) { ImGuiContext& g = *GImGui; g.ActiveIdUsingKeyInputMask.SetBit(key); } + IMGUI_API void GetKeyChordName(ImGuiKeyChord key_chord, char* out_buf, int out_buf_size); + inline ImGuiKey MouseButtonToKey(ImGuiMouseButton button) { IM_ASSERT(button >= 0 && button < ImGuiMouseButton_COUNT); return (ImGuiKey)(ImGuiKey_MouseLeft + button); } IMGUI_API bool IsMouseDragPastThreshold(ImGuiMouseButton button, float lock_threshold = -1.0f); - inline bool IsNavInputDown(ImGuiNavInput n) { ImGuiContext& g = *GImGui; return g.IO.NavInputs[n] > 0.0f; } - inline bool IsNavInputTest(ImGuiNavInput n, ImGuiNavReadMode rm) { return (GetNavInputAmount(n, rm) > 0.0f); } - IMGUI_API ImGuiModFlags GetMergedModFlags(); -#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO - inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } // [removed in 1.87] -#endif + IMGUI_API ImVec2 GetKeyMagnitude2d(ImGuiKey key_left, ImGuiKey key_right, ImGuiKey key_up, ImGuiKey key_down); + IMGUI_API float GetNavTweakPressedAmount(ImGuiAxis axis); + IMGUI_API int CalcTypematicRepeatAmount(float t0, float t1, float repeat_delay, float repeat_rate); + IMGUI_API void GetTypematicRepeatRate(ImGuiInputFlags flags, float* repeat_delay, float* repeat_rate); + IMGUI_API void SetActiveIdUsingAllKeyboardKeys(); + inline bool IsActiveIdUsingNavDir(ImGuiDir dir) { ImGuiContext& g = *GImGui; return (g.ActiveIdUsingNavDirMask & (1 << dir)) != 0; } + + // [EXPERIMENTAL] Low-Level: Key/Input Ownership + // - The idea is that instead of "eating" a given input, we can link to an owner id. + // - Ownership is most often claimed as a result of reacting to a press/down event (but occasionally may be claimed ahead). + // - Input queries can then read input by specifying ImGuiKeyOwner_Any (== 0), ImGuiKeyOwner_None (== -1) or a custom ID. + // - Legacy input queries (without specifying an owner or _Any or _None) are equivalent to using ImGuiKeyOwner_Any (== 0). + // - Input ownership is automatically released on the frame after a key is released. Therefore: + // - for ownership registration happening as a result of a down/press event, the SetKeyOwner() call may be done once (common case). + // - for ownership registration happening ahead of a down/press event, the SetKeyOwner() call needs to be made every frame (happens if e.g. claiming ownership on hover). + // - SetItemKeyOwner() is a shortcut for common simple case. A custom widget will probably want to call SetKeyOwner() multiple times directly based on its interaction state. + // - This is marked experimental because not all widgets are fully honoring the Set/Test idioms. We will need to move forward step by step. + // Please open a GitHub Issue to submit your usage scenario or if there's a use case you need solved. + IMGUI_API ImGuiID GetKeyOwner(ImGuiKey key); + IMGUI_API void SetKeyOwner(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0); + IMGUI_API void SetItemKeyOwner(ImGuiKey key, ImGuiInputFlags flags = 0); // Set key owner to last item if it is hovered or active. Equivalent to 'if (IsItemHovered() || IsItemActive()) { SetKeyOwner(key, GetItemID());'. + IMGUI_API bool TestKeyOwner(ImGuiKey key, ImGuiID owner_id); // Test that key is either not owned, either owned by 'owner_id' + inline ImGuiKeyOwnerData* GetKeyOwnerData(ImGuiKey key) { if (key & ImGuiMod_Mask_) key = ConvertSingleModFlagToKey(key); IM_ASSERT(IsNamedKey(key)); return &GImGui->KeysOwnerData[key - ImGuiKey_NamedKey_BEGIN]; } + + // [EXPERIMENTAL] High-Level: Input Access functions w/ support for Key/Input Ownership + // - Important: legacy IsKeyPressed(ImGuiKey, bool repeat=true) _DEFAULTS_ to repeat, new IsKeyPressed() requires _EXPLICIT_ ImGuiInputFlags_Repeat flag. + // - Expected to be later promoted to public API, the prototypes are designed to replace existing ones (since owner_id can default to Any == 0) + // - Specifying a value for 'ImGuiID owner' will test that EITHER the key is NOT owned (UNLESS locked), EITHER the key is owned by 'owner'. + // Legacy functions use ImGuiKeyOwner_Any meaning that they typically ignore ownership, unless a call to SetKeyOwner() explicitly used ImGuiInputFlags_LockThisFrame or ImGuiInputFlags_LockUntilRelease. + // - Binding generators may want to ignore those for now, or suffix them with Ex() until we decide if this gets moved into public API. + IMGUI_API bool IsKeyDown(ImGuiKey key, ImGuiID owner_id); + IMGUI_API bool IsKeyPressed(ImGuiKey key, ImGuiID owner_id, ImGuiInputFlags flags = 0); // Important: when transitioning from old to new IsKeyPressed(): old API has "bool repeat = true", so would default to repeat. New API requiress explicit ImGuiInputFlags_Repeat. + IMGUI_API bool IsKeyReleased(ImGuiKey key, ImGuiID owner_id); + IMGUI_API bool IsMouseDown(ImGuiMouseButton button, ImGuiID owner_id); + IMGUI_API bool IsMouseClicked(ImGuiMouseButton button, ImGuiID owner_id, ImGuiInputFlags flags = 0); + IMGUI_API bool IsMouseReleased(ImGuiMouseButton button, ImGuiID owner_id); + + // [EXPERIMENTAL] Shortcut Routing + // - ImGuiKeyChord = a ImGuiKey optionally OR-red with ImGuiMod_Alt/ImGuiMod_Ctrl/ImGuiMod_Shift/ImGuiMod_Super. + // ImGuiKey_C (accepted by functions taking ImGuiKey or ImGuiKeyChord) + // ImGuiKey_C | ImGuiMod_Ctrl (accepted by functions taking ImGuiKeyChord) + // ONLY ImGuiMod_XXX values are legal to 'OR' with an ImGuiKey. You CANNOT 'OR' two ImGuiKey values. + // - When using one of the routing flags (e.g. ImGuiInputFlags_RouteFocused): routes requested ahead of time given a chord (key + modifiers) and a routing policy. + // - Routes are resolved during NewFrame(): if keyboard modifiers are matching current ones: SetKeyOwner() is called + route is granted for the frame. + // - Route is granted to a single owner. When multiple requests are made we have policies to select the winning route. + // - Multiple read sites may use the same owner id and will all get the granted route. + // - For routing: when owner_id is 0 we use the current Focus Scope ID as a default owner in order to identify our location. + IMGUI_API bool Shortcut(ImGuiKeyChord key_chord, ImGuiID owner_id = 0, ImGuiInputFlags flags = 0); + IMGUI_API bool SetShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id = 0, ImGuiInputFlags flags = 0); + IMGUI_API bool TestShortcutRouting(ImGuiKeyChord key_chord, ImGuiID owner_id); + IMGUI_API ImGuiKeyRoutingData* GetShortcutRoutingData(ImGuiKeyChord key_chord); + + // [EXPERIMENTAL] Focus Scope + // This is generally used to identify a unique input location (for e.g. a selection set) + // There is one per window (automatically set in Begin), but: + // - Selection patterns generally need to react (e.g. clear a selection) when landing on one item of the set. + // So in order to identify a set multiple lists in same window may each need a focus scope. + // If you imagine an hypothetical BeginSelectionGroup()/EndSelectionGroup() api, it would likely call PushFocusScope()/EndFocusScope() + // - Shortcut routing also use focus scope as a default location identifier if an owner is not provided. + // We don't use the ID Stack for this as it is common to want them separate. + IMGUI_API void PushFocusScope(ImGuiID id); + IMGUI_API void PopFocusScope(); + inline ImGuiID GetCurrentFocusScope() { ImGuiContext& g = *GImGui; return g.CurrentFocusScopeId; } // Focus scope we are outputting into, set by PushFocusScope() // Drag and Drop IMGUI_API bool IsDragDropActive(); IMGUI_API bool BeginDragDropTargetCustom(const ImRect& bb, ImGuiID id); IMGUI_API void ClearDragDrop(); IMGUI_API bool IsDragDropPayloadBeingAccepted(); + IMGUI_API void RenderDragDropTargetRect(const ImRect& bb); // Internal Columns API (this is not exposed because we will encourage transitioning to the Tables API) IMGUI_API void SetWindowClipRectBeforeSetChannel(ImGuiWindow* window, const ImRect& clip_rect); @@ -2753,6 +3001,7 @@ namespace ImGui IMGUI_API void TableUpdateColumnsWeightFromWidth(ImGuiTable* table); IMGUI_API void TableDrawBorders(ImGuiTable* table); IMGUI_API void TableDrawContextMenu(ImGuiTable* table); + IMGUI_API bool TableBeginContextMenuPopup(ImGuiTable* table); IMGUI_API void TableMergeDrawChannels(ImGuiTable* table); inline ImGuiTableInstanceData* TableGetInstanceData(ImGuiTable* table, int instance_no) { if (instance_no == 0) return &table->InstanceDataFirst; return &table->InstanceDataExtra[instance_no - 1]; } IMGUI_API void TableSortSpecsSanitize(ImGuiTable* table); @@ -2792,8 +3041,9 @@ namespace ImGui IMGUI_API void TabBarQueueReorder(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, int offset); IMGUI_API void TabBarQueueReorderFromMousePos(ImGuiTabBar* tab_bar, const ImGuiTabItem* tab, ImVec2 mouse_pos); IMGUI_API bool TabBarProcessReorder(ImGuiTabBar* tab_bar); - IMGUI_API bool TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags); - IMGUI_API ImVec2 TabItemCalcSize(const char* label, bool has_close_button); + IMGUI_API bool TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window); + IMGUI_API ImVec2 TabItemCalcSize(const char* label, bool has_close_button_or_unsaved_marker); + IMGUI_API ImVec2 TabItemCalcSize(ImGuiWindow* window); IMGUI_API void TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col); IMGUI_API void TabItemLabelAndCloseButton(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImVec2 frame_padding, const char* label, ImGuiID tab_id, ImGuiID close_button_id, bool is_contents_visible, bool* out_just_closed, bool* out_text_clipped); @@ -2823,28 +3073,31 @@ namespace ImGui // Widgets IMGUI_API void TextEx(const char* text, const char* text_end = NULL, ImGuiTextFlags flags = 0); IMGUI_API bool ButtonEx(const char* label, const ImVec2& size_arg = ImVec2(0, 0), ImGuiButtonFlags flags = 0); + IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0); + IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col); + IMGUI_API void SeparatorEx(ImGuiSeparatorFlags flags); + IMGUI_API bool CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value); + IMGUI_API bool CheckboxFlags(const char* label, ImU64* flags, ImU64 flags_value); + + // Widgets: Window Decorations IMGUI_API bool CloseButton(ImGuiID id, const ImVec2& pos); IMGUI_API bool CollapseButton(ImGuiID id, const ImVec2& pos); - IMGUI_API bool ArrowButtonEx(const char* str_id, ImGuiDir dir, ImVec2 size_arg, ImGuiButtonFlags flags = 0); IMGUI_API void Scrollbar(ImGuiAxis axis); IMGUI_API bool ScrollbarEx(const ImRect& bb, ImGuiID id, ImGuiAxis axis, ImS64* p_scroll_v, ImS64 avail_v, ImS64 contents_v, ImDrawFlags flags); - IMGUI_API bool ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec2& padding, const ImVec4& bg_col, const ImVec4& tint_col); IMGUI_API ImRect GetWindowScrollbarRect(ImGuiWindow* window, ImGuiAxis axis); IMGUI_API ImGuiID GetWindowScrollbarID(ImGuiWindow* window, ImGuiAxis axis); IMGUI_API ImGuiID GetWindowResizeCornerID(ImGuiWindow* window, int n); // 0..3: corners IMGUI_API ImGuiID GetWindowResizeBorderID(ImGuiWindow* window, ImGuiDir dir); - IMGUI_API void SeparatorEx(ImGuiSeparatorFlags flags); - IMGUI_API bool CheckboxFlags(const char* label, ImS64* flags, ImS64 flags_value); - IMGUI_API bool CheckboxFlags(const char* label, ImU64* flags, ImU64 flags_value); // Widgets low-level behaviors IMGUI_API bool ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool* out_held, ImGuiButtonFlags flags = 0); IMGUI_API bool DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v_speed, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags); IMGUI_API bool SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type, void* p_v, const void* p_min, const void* p_max, const char* format, ImGuiSliderFlags flags, ImRect* out_grab_bb); - IMGUI_API bool SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f); + IMGUI_API bool SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend = 0.0f, float hover_visibility_delay = 0.0f, ImU32 bg_col = 0); IMGUI_API bool TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* label, const char* label_end = NULL); - IMGUI_API bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0); // Consume previous SetNextItemOpen() data, if any. May return true when logging IMGUI_API void TreePushOverrideID(ImGuiID id); + IMGUI_API void TreeNodeSetOpen(ImGuiID id, bool open); + IMGUI_API bool TreeNodeUpdateNextOpen(ImGuiID id, ImGuiTreeNodeFlags flags); // Return open state. Consume previous SetNextItemOpen() data, if any. May return true when logging. // Template functions are instantiated in imgui_widgets.cpp for a finite number of types. // To use them externally (for custom widget) you may need an "extern template" statement in your code in order to link to existing instances and silence Clang warnings (see #2036). @@ -2869,7 +3122,7 @@ namespace ImGui IMGUI_API bool TempInputText(const ImRect& bb, ImGuiID id, const char* label, char* buf, int buf_size, ImGuiInputTextFlags flags); IMGUI_API bool TempInputScalar(const ImRect& bb, ImGuiID id, const char* label, ImGuiDataType data_type, void* p_data, const char* format, const void* p_clamp_min = NULL, const void* p_clamp_max = NULL); inline bool TempInputIsActive(ImGuiID id) { ImGuiContext& g = *GImGui; return (g.ActiveId == id && g.TempInputId == id); } - inline ImGuiInputTextState* GetInputTextState(ImGuiID id) { ImGuiContext& g = *GImGui; return (g.InputTextState.ID == id) ? &g.InputTextState : NULL; } // Get input text state if active + inline ImGuiInputTextState* GetInputTextState(ImGuiID id) { ImGuiContext& g = *GImGui; return (id != 0 && g.InputTextState.ID == id) ? &g.InputTextState : NULL; } // Get input text state if active // Color IMGUI_API void ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags flags); @@ -2891,10 +3144,14 @@ namespace ImGui // Debug Log IMGUI_API void DebugLog(const char* fmt, ...) IM_FMTARGS(1); IMGUI_API void DebugLogV(const char* fmt, va_list args) IM_FMTLIST(1); - + // Debug Tools IMGUI_API void ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); IMGUI_API void ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); + IMGUI_API void ErrorCheckUsingSetCursorPosToExtendParentBoundaries(); + IMGUI_API void DebugLocateItem(ImGuiID target_id); // Call sparingly: only 1 at the same time! + IMGUI_API void DebugLocateItemOnHover(ImGuiID target_id); // Only call on reaction to a mouse Hover: because only 1 at the same time! + IMGUI_API void DebugLocateItemResolveWithLastItem(); inline void DebugDrawItemRect(ImU32 col = IM_COL32(255,0,0,255)) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; GetForegroundDrawList(window)->AddRect(g.LastItemData.Rect.Min, g.LastItemData.Rect.Max, col); } inline void DebugStartItemPicker() { ImGuiContext& g = *GImGui; g.DebugItemPickerActive = true; } IMGUI_API void ShowFontAtlas(ImFontAtlas* atlas); @@ -2914,8 +3171,26 @@ namespace ImGui IMGUI_API void DebugNodeWindowsList(ImVector* windows, const char* label); IMGUI_API void DebugNodeWindowsListByBeginStackParent(ImGuiWindow** windows, int windows_size, ImGuiWindow* parent_in_begin_stack); IMGUI_API void DebugNodeViewport(ImGuiViewportP* viewport); + IMGUI_API void DebugRenderKeyboardPreview(ImDrawList* draw_list); IMGUI_API void DebugRenderViewportThumbnail(ImDrawList* draw_list, ImGuiViewportP* viewport, const ImRect& bb); + // Obsolete functions +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS + inline void SetItemUsingMouseWheel() { SetItemKeyOwner(ImGuiKey_MouseWheelY); } // Changed in 1.89 + inline bool TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags = 0) { return TreeNodeUpdateNextOpen(id, flags); } // Renamed in 1.89 + + // Refactored focus/nav/tabbing system in 1.82 and 1.84. If you have old/custom copy-and-pasted widgets that used FocusableItemRegister(): + // (Old) IMGUI_VERSION_NUM < 18209: using 'ItemAdd(....)' and 'bool tab_focused = FocusableItemRegister(...)' + // (Old) IMGUI_VERSION_NUM >= 18209: using 'ItemAdd(..., ImGuiItemAddFlags_Focusable)' and 'bool tab_focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_Focused) != 0' + // (New) IMGUI_VERSION_NUM >= 18413: using 'ItemAdd(..., ImGuiItemFlags_Inputable)' and 'bool tab_focused = (GetItemStatusFlags() & ImGuiItemStatusFlags_FocusedTabbing) != 0 || g.NavActivateInputId == id' (WIP) + // Widget code are simplified as there's no need to call FocusableItemUnregister() while managing the transition from regular widget to TempInputText() + inline bool FocusableItemRegister(ImGuiWindow* window, ImGuiID id) { IM_ASSERT(0); IM_UNUSED(window); IM_UNUSED(id); return false; } // -> pass ImGuiItemAddFlags_Inputable flag to ItemAdd() + inline void FocusableItemUnregister(ImGuiWindow* window) { IM_ASSERT(0); IM_UNUSED(window); } // -> unnecessary: TempInputText() uses ImGuiInputTextFlags_MergedItem +#endif +#ifndef IMGUI_DISABLE_OBSOLETE_KEYIO + inline bool IsKeyPressedMap(ImGuiKey key, bool repeat = true) { IM_ASSERT(IsNamedKey(key)); return IsKeyPressed(key, repeat); } // Removed in 1.87: Mapping from named key is always identity! +#endif + } // namespace ImGui diff --git a/extern/src/imgui/imgui_tables.cpp b/extern/src/imgui/imgui_tables.cpp index c3e24af3..47fc8a28 100644 --- a/extern/src/imgui/imgui_tables.cpp +++ b/extern/src/imgui/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.88 +// dear imgui, v1.89.2 // (tables and columns code) /* @@ -395,6 +395,14 @@ bool ImGui::BeginTableEx(const char* name, ImGuiID id, int columns_count, ImG table->OuterRect = table->InnerWindow->Rect(); table->InnerRect = table->InnerWindow->InnerRect; IM_ASSERT(table->InnerWindow->WindowPadding.x == 0.0f && table->InnerWindow->WindowPadding.y == 0.0f && table->InnerWindow->WindowBorderSize == 0.0f); + + // When using multiple instances, ensure they have the same amount of horizontal decorations (aka vertical scrollbar) so stretched columns can be aligned) + if (instance_no == 0) + { + table->HasScrollbarYPrev = table->HasScrollbarYCurr; + table->HasScrollbarYCurr = false; + } + table->HasScrollbarYCurr |= (table->InnerWindow->ScrollMax.y > 0.0f); } else { @@ -893,7 +901,8 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) // [Part 4] Apply final widths based on requested widths const ImRect work_rect = table->WorkRect; const float width_spacings = (table->OuterPaddingX * 2.0f) + (table->CellSpacingX1 + table->CellSpacingX2) * (table->ColumnsEnabledCount - 1); - const float width_avail = ((table->Flags & ImGuiTableFlags_ScrollX) && table->InnerWidth == 0.0f) ? table->InnerClipRect.GetWidth() : work_rect.GetWidth(); + const float width_removed = (table->HasScrollbarYPrev && !table->InnerWindow->ScrollbarY) ? g.Style.ScrollbarSize : 0.0f; // To synchronize decoration width of synched tables with mismatching scrollbar state (#5920) + const float width_avail = ImMax(1.0f, (((table->Flags & ImGuiTableFlags_ScrollX) && table->InnerWidth == 0.0f) ? table->InnerClipRect.GetWidth() : work_rect.GetWidth()) - width_removed); const float width_avail_for_stretched_columns = width_avail - width_spacings - sum_width_requests; float width_remaining_for_stretched_columns = width_avail_for_stretched_columns; table->ColumnsGivenWidth = width_spacings + (table->CellPaddingX * 2.0f) * table->ColumnsEnabledCount; @@ -936,11 +945,19 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) width_remaining_for_stretched_columns -= 1.0f; } + // Determine if table is hovered which will be used to flag columns as hovered. + // - In principle we'd like to use the equivalent of IsItemHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), + // but because our item is partially submitted at this point we use ItemHoverable() and a workaround (temporarily + // clear ActiveId, which is equivalent to the change provided by _AllowWhenBLockedByActiveItem). + // - This allows columns to be marked as hovered when e.g. clicking a button inside the column, or using drag and drop. ImGuiTableInstanceData* table_instance = TableGetInstanceData(table, table->InstanceCurrent); table->HoveredColumnBody = -1; table->HoveredColumnBorder = -1; const ImRect mouse_hit_rect(table->OuterRect.Min.x, table->OuterRect.Min.y, table->OuterRect.Max.x, ImMax(table->OuterRect.Max.y, table->OuterRect.Min.y + table_instance->LastOuterHeight)); + const ImGuiID backup_active_id = g.ActiveId; + g.ActiveId = 0; const bool is_hovering_table = ItemHoverable(mouse_hit_rect, 0); + g.ActiveId = backup_active_id; // [Part 6] Setup final position, offset, skip/clip states and clipping rectangles, detect hovered column // Process columns in their visible orders as we are comparing the visible order and adjusting host_clip_rect while looping. @@ -956,7 +973,7 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) const int column_n = table->DisplayOrderToIndex[order_n]; ImGuiTableColumn* column = &table->Columns[column_n]; - column->NavLayerCurrent = (ImS8)((table->FreezeRowsCount > 0 || column_n < table->FreezeColumnsCount) ? ImGuiNavLayer_Menu : ImGuiNavLayer_Main); + column->NavLayerCurrent = (ImS8)(table->FreezeRowsCount > 0 ? ImGuiNavLayer_Menu : ImGuiNavLayer_Main); // Use Count NOT request so Header line changes layer when frozen if (offset_x_frozen && table->FreezeColumnsCount == visible_n) { @@ -1105,18 +1122,10 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) table->IsUsingHeaders = false; // [Part 11] Context menu - if (table->IsContextPopupOpen && table->InstanceCurrent == table->InstanceInteracted) + if (TableBeginContextMenuPopup(table)) { - const ImGuiID context_menu_id = ImHashStr("##ContextMenu", 0, table->ID); - if (BeginPopupEx(context_menu_id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings)) - { - TableDrawContextMenu(table); - EndPopup(); - } - else - { - table->IsContextPopupOpen = false; - } + TableDrawContextMenu(table); + EndPopup(); } // [Part 13] Sanitize and build sort specs before we have a change to use them for display. @@ -1124,6 +1133,13 @@ void ImGui::TableUpdateLayout(ImGuiTable* table) if (table->IsSortSpecsDirty && (table->Flags & ImGuiTableFlags_Sortable)) TableSortSpecsBuild(table); + // [Part 14] Setup inner window decoration size (for scrolling / nav tracking to properly take account of frozen rows/columns) + if (table->FreezeColumnsRequest > 0) + table->InnerWindow->DecoInnerSizeX1 = table->Columns[table->DisplayOrderToIndex[table->FreezeColumnsRequest - 1]].MaxX - table->OuterRect.Min.x; + if (table->FreezeRowsRequest > 0) + table->InnerWindow->DecoInnerSizeY1 = table_instance->LastFrozenHeight; + table_instance->LastFrozenHeight = 0.0f; + // Initial state ImGuiWindow* inner_window = table->InnerWindow; if (table->Flags & ImGuiTableFlags_NoClip) @@ -1171,8 +1187,8 @@ void ImGui::TableUpdateBorders(ImGuiTable* table) ImGuiID column_id = TableGetColumnResizeID(table, column_n, table->InstanceCurrent); ImRect hit_rect(column->MaxX - hit_half_width, hit_y1, column->MaxX + hit_half_width, border_y2_hit); + ItemAdd(hit_rect, column_id, NULL, ImGuiItemFlags_NoNav); //GetForegroundDrawList()->AddRect(hit_rect.Min, hit_rect.Max, IM_COL32(255, 0, 0, 100)); - KeepAliveID(column_id); bool hovered = false, held = false; bool pressed = ButtonBehavior(hit_rect, column_id, &hovered, &held, ImGuiButtonFlags_FlattenChildren | ImGuiButtonFlags_AllowItemOverlap | ImGuiButtonFlags_PressedOnClick | ImGuiButtonFlags_PressedOnDoubleClick | ImGuiButtonFlags_NoNavFocus); @@ -1725,6 +1741,8 @@ void ImGui::TableBeginRow(ImGuiTable* table) table->RowTextBaseline = 0.0f; table->RowIndentOffsetX = window->DC.Indent.x - table->HostIndentX; // Lock indent window->DC.PrevLineTextBaseOffset = 0.0f; + window->DC.CurrLineSize = ImVec2(0.0f, 0.0f); + window->DC.IsSameLine = window->DC.IsSetPos = false; window->DC.CursorMaxPos.y = next_y1; // Making the header BG color non-transparent will allow us to overlay it multiple times when handling smooth dragging. @@ -1837,17 +1855,15 @@ void ImGui::TableEndRow(ImGuiTable* table) // get the new cursor position. if (unfreeze_rows_request) for (int column_n = 0; column_n < table->ColumnsCount; column_n++) - { - ImGuiTableColumn* column = &table->Columns[column_n]; - column->NavLayerCurrent = (ImS8)((column_n < table->FreezeColumnsCount) ? ImGuiNavLayer_Menu : ImGuiNavLayer_Main); - } + table->Columns[column_n].NavLayerCurrent = ImGuiNavLayer_Main; if (unfreeze_rows_actual) { IM_ASSERT(table->IsUnfrozenRows == false); + const float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y); table->IsUnfrozenRows = true; + TableGetInstanceData(table, table->InstanceCurrent)->LastFrozenHeight = y0 - table->OuterRect.Min.y; // BgClipRect starts as table->InnerClipRect, reduce it now and make BgClipRectForDrawCmd == BgClipRect - float y0 = ImMax(table->RowPosY2 + 1, window->InnerClipRect.Min.y); table->BgClipRect.Min.y = table->Bg2ClipRectForDrawCmd.Min.y = ImMin(y0, window->InnerClipRect.Max.y); table->BgClipRect.Max.y = table->Bg2ClipRectForDrawCmd.Max.y = window->InnerClipRect.Max.y; table->Bg2DrawChannelCurrent = table->Bg2DrawChannelUnfrozen; @@ -2006,6 +2022,9 @@ void ImGui::TableEndCell(ImGuiTable* table) ImGuiTableColumn* column = &table->Columns[table->CurrentColumn]; ImGuiWindow* window = table->InnerWindow; + if (window->DC.IsSetPos) + ErrorCheckUsingSetCursorPosToExtendParentBoundaries(); + // Report maximum position so we can infer content size per column. float* p_max_pos_x; if (table->RowFlags & ImGuiTableRowFlags_Headers) @@ -3000,7 +3019,7 @@ void ImGui::TableHeader(const char* label) RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size); const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x); - if (text_clipped && hovered && g.HoveredIdNotActiveTimer > g.TooltipSlowDelay) + if (text_clipped && hovered && g.ActiveId == 0 && IsItemHovered(ImGuiHoveredFlags_DelayNormal)) SetTooltip("%.*s", (int)(label_end - label), label); // We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden @@ -3035,6 +3054,17 @@ void ImGui::TableOpenContextMenu(int column_n) } } +bool ImGui::TableBeginContextMenuPopup(ImGuiTable* table) +{ + if (!table->IsContextPopupOpen || table->InstanceCurrent != table->InstanceInteracted) + return false; + const ImGuiID context_menu_id = ImHashStr("##ContextMenu", 0, table->ID); + if (BeginPopupEx(context_menu_id, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings)) + return true; + table->IsContextPopupOpen = false; + return false; +} + // Output context menu into current window (generally a popup) // FIXME-TABLE: Ideally this should be writable by the user. Full programmatic access to that data? void ImGui::TableDrawContextMenu(ImGuiTable* table) @@ -3054,15 +3084,15 @@ void ImGui::TableDrawContextMenu(ImGuiTable* table) if (column != NULL) { const bool can_resize = !(column->Flags & ImGuiTableColumnFlags_NoResize) && column->IsEnabled; - if (MenuItem("Size column to fit###SizeOne", NULL, false, can_resize)) + if (MenuItem(LocalizeGetMsg(ImGuiLocKey_TableSizeOne), NULL, false, can_resize)) // "###SizeOne" TableSetColumnWidthAutoSingle(table, column_n); } const char* size_all_desc; if (table->ColumnsEnabledFixedCount == table->ColumnsEnabledCount && (table->Flags & ImGuiTableFlags_SizingMask_) != ImGuiTableFlags_SizingFixedSame) - size_all_desc = "Size all columns to fit###SizeAll"; // All fixed + size_all_desc = LocalizeGetMsg(ImGuiLocKey_TableSizeAllFit); // "###SizeAll" All fixed else - size_all_desc = "Size all columns to default###SizeAll"; // All stretch or mixed + size_all_desc = LocalizeGetMsg(ImGuiLocKey_TableSizeAllDefault); // "###SizeAll" All stretch or mixed if (MenuItem(size_all_desc, NULL)) TableSetColumnWidthAutoAll(table); want_separator = true; @@ -3071,7 +3101,7 @@ void ImGui::TableDrawContextMenu(ImGuiTable* table) // Ordering if (table->Flags & ImGuiTableFlags_Reorderable) { - if (MenuItem("Reset order", NULL, false, !table->IsDefaultDisplayOrder)) + if (MenuItem(LocalizeGetMsg(ImGuiLocKey_TableResetOrder), NULL, false, !table->IsDefaultDisplayOrder)) table->IsResetDisplayOrderRequest = true; want_separator = true; } @@ -3954,6 +3984,7 @@ void ImGui::NextColumn() { // New row/line: column 0 honor IndentX. window->DC.ColumnsOffset.x = ImMax(column_padding - window->WindowPadding.x, 0.0f); + window->DC.IsSameLine = false; columns->LineMinY = columns->LineMaxY; } window->DC.CursorPos.x = IM_FLOOR(window->Pos.x + window->DC.Indent.x + window->DC.ColumnsOffset.x); @@ -4005,8 +4036,7 @@ void ImGui::EndColumns() const ImGuiID column_id = columns->ID + ImGuiID(n); const float column_hit_hw = COLUMNS_HIT_RECT_HALF_WIDTH; const ImRect column_hit_rect(ImVec2(x - column_hit_hw, y1), ImVec2(x + column_hit_hw, y2)); - KeepAliveID(column_id); - if (IsClippedEx(column_hit_rect, column_id)) // FIXME: Can be removed or replaced with a lower-level test + if (!ItemAdd(column_hit_rect, column_id, NULL, ImGuiItemFlags_NoNav)) continue; bool hovered = false, held = false; diff --git a/extern/src/imgui/imgui_widgets.cpp b/extern/src/imgui/imgui_widgets.cpp index ef91630f..0a1ea678 100644 --- a/extern/src/imgui/imgui_widgets.cpp +++ b/extern/src/imgui/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.88 +// dear imgui, v1.89.2 // (widgets code) /* @@ -41,7 +41,6 @@ Index of this file: #include "imgui_internal.h" // System includes -#include // toupper #if defined(_MSC_VER) && _MSC_VER <= 1500 // MSVC 2008 or earlier #include // intptr_t #else @@ -127,7 +126,7 @@ static const ImU64 IM_U64_MAX = (2ULL * 9223372036854775807LL + 1); // For InputTextEx() static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data, ImGuiInputSource input_source); static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** out_text_end); -static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); +static ImVec2 InputTextCalcTextSizeW(ImGuiContext* ctx, const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining = NULL, ImVec2* out_offset = NULL, bool stop_on_new_line = false); //------------------------------------------------------------------------- // [SECTION] Widgets: Text, etc. @@ -542,18 +541,28 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool hovered = false; // Mouse handling + const ImGuiID test_owner_id = (flags & ImGuiButtonFlags_NoTestKeyOwner) ? ImGuiKeyOwner_Any : id; if (hovered) { + // Poll mouse buttons + // - 'mouse_button_clicked' is generally carried into ActiveIdMouseButton when setting ActiveId. + // - Technically we only need some values in one code path, but since this is gated by hovered test this is fine. + int mouse_button_clicked = -1; + int mouse_button_released = -1; + for (int button = 0; button < 3; button++) + if (flags & (ImGuiButtonFlags_MouseButtonLeft << button)) // Handle ImGuiButtonFlags_MouseButtonRight and ImGuiButtonFlags_MouseButtonMiddle here. + { + if (IsMouseClicked(button, test_owner_id) && mouse_button_clicked == -1) { mouse_button_clicked = button; } + if (IsMouseReleased(button, test_owner_id) && mouse_button_released == -1) { mouse_button_released = button; } + } + + // Process initial action if (!(flags & ImGuiButtonFlags_NoKeyModifiers) || (!g.IO.KeyCtrl && !g.IO.KeyShift && !g.IO.KeyAlt)) { - // Poll buttons - int mouse_button_clicked = -1; - if ((flags & ImGuiButtonFlags_MouseButtonLeft) && g.IO.MouseClicked[0]) { mouse_button_clicked = 0; } - else if ((flags & ImGuiButtonFlags_MouseButtonRight) && g.IO.MouseClicked[1]) { mouse_button_clicked = 1; } - else if ((flags & ImGuiButtonFlags_MouseButtonMiddle) && g.IO.MouseClicked[2]) { mouse_button_clicked = 2; } - if (mouse_button_clicked != -1 && g.ActiveId != id) { + if (!(flags & ImGuiButtonFlags_NoSetKeyOwner)) + SetKeyOwner(MouseButtonToKey(mouse_button_clicked), id); if (flags & (ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnClickReleaseAnywhere)) { SetActiveID(id, window); @@ -577,10 +586,6 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool } if (flags & ImGuiButtonFlags_PressedOnRelease) { - int mouse_button_released = -1; - if ((flags & ImGuiButtonFlags_MouseButtonLeft) && g.IO.MouseReleased[0]) { mouse_button_released = 0; } - else if ((flags & ImGuiButtonFlags_MouseButtonRight) && g.IO.MouseReleased[1]) { mouse_button_released = 1; } - else if ((flags & ImGuiButtonFlags_MouseButtonMiddle) && g.IO.MouseReleased[2]) { mouse_button_released = 2; } if (mouse_button_released != -1) { const bool has_repeated_at_least_once = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button_released] >= g.IO.KeyRepeatDelay; // Repeat mode trumps on release behavior @@ -595,7 +600,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool // 'Repeat' mode acts when held regardless of _PressedOn flags (see table above). // Relies on repeat logic of IsMouseClicked() but we may as well do it ourselves if we end up exposing finer RepeatDelay/RepeatRate settings. if (g.ActiveId == id && (flags & ImGuiButtonFlags_Repeat)) - if (g.IO.MouseDownDuration[g.ActiveIdMouseButton] > 0.0f && IsMouseClicked(g.ActiveIdMouseButton, true)) + if (g.IO.MouseDownDuration[g.ActiveIdMouseButton] > 0.0f && IsMouseClicked(g.ActiveIdMouseButton, test_owner_id, ImGuiInputFlags_Repeat)) pressed = true; } @@ -611,7 +616,15 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool if (g.NavActivateDownId == id) { bool nav_activated_by_code = (g.NavActivateId == id); - bool nav_activated_by_inputs = IsNavInputTest(ImGuiNavInput_Activate, (flags & ImGuiButtonFlags_Repeat) ? ImGuiNavReadMode_Repeat : ImGuiNavReadMode_Pressed); + bool nav_activated_by_inputs = (g.NavActivatePressedId == id); + if (!nav_activated_by_inputs && (flags & ImGuiButtonFlags_Repeat)) + { + // Avoid pressing both keys from triggering double amount of repeat events + const ImGuiKeyData* key1 = GetKeyData(ImGuiKey_Space); + const ImGuiKeyData* key2 = GetKeyData(ImGuiKey_NavGamepadActivate); + const float t1 = ImMax(key1->DownDuration, key2->DownDuration); + nav_activated_by_inputs = CalcTypematicRepeatAmount(t1 - g.IO.DeltaTime, t1, g.IO.KeyRepeatDelay, g.IO.KeyRepeatRate) > 0; + } if (nav_activated_by_code || nav_activated_by_inputs) { // Set active id so it can be queried by user via IsItemActive(), equivalent of holding the mouse button. @@ -633,8 +646,7 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool g.ActiveIdClickOffset = g.IO.MousePos - bb.Min; const int mouse_button = g.ActiveIdMouseButton; - IM_ASSERT(mouse_button >= 0 && mouse_button < ImGuiMouseButton_COUNT); - if (g.IO.MouseDown[mouse_button]) + if (IsMouseDown(mouse_button, test_owner_id)) { held = true; } @@ -647,7 +659,8 @@ bool ImGui::ButtonBehavior(const ImRect& bb, ImGuiID id, bool* out_hovered, bool // Report as pressed when releasing the mouse (this is the most common path) bool is_double_click_release = (flags & ImGuiButtonFlags_PressedOnDoubleClick) && g.IO.MouseReleased[mouse_button] && g.IO.MouseClickedLastCount[mouse_button] == 2; bool is_repeating_already = (flags & ImGuiButtonFlags_Repeat) && g.IO.MouseDownDurationPrev[mouse_button] >= g.IO.KeyRepeatDelay; // Repeat mode trumps - if (!is_double_click_release && !is_repeating_already) + bool is_button_avail_or_owned = TestKeyOwner(MouseButtonToKey(mouse_button), test_owner_id); + if (!is_double_click_release && !is_repeating_already && is_button_avail_or_owned) pressed = true; } ClearActiveID(); @@ -918,8 +931,6 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6 if (window->SkipItems) return false; - KeepAliveID(id); - const float bb_frame_width = bb_frame.GetWidth(); const float bb_frame_height = bb_frame.GetHeight(); if (bb_frame_width <= 0.0f || bb_frame_height <= 0.0f) @@ -951,6 +962,7 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6 // Handle input right away. None of the code of Begin() is relying on scrolling position before calling Scrollbar(). bool held = false; bool hovered = false; + ItemAdd(bb_frame, id, NULL, ImGuiItemFlags_NoNav); ButtonBehavior(bb, id, &hovered, &held, ImGuiButtonFlags_NoNavFocus); const ImS64 scroll_max = ImMax((ImS64)1, size_contents_v - size_avail_v); @@ -1030,14 +1042,15 @@ void ImGui::Image(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& // ImageButton() is flawed as 'id' is always derived from 'texture_id' (see #2464 #1390) // We provide this internal helper to write your own variant while we figure out how to redesign the public ImageButton() API. -bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec2& padding, const ImVec4& bg_col, const ImVec4& tint_col) +bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col) { ImGuiContext& g = *GImGui; ImGuiWindow* window = GetCurrentWindow(); if (window->SkipItems) return false; - const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding * 2); + const ImVec2 padding = g.Style.FramePadding; + const ImRect bb(window->DC.CursorPos, window->DC.CursorPos + size + padding * 2.0f); ItemSize(bb); if (!ItemAdd(bb, id)) return false; @@ -1056,9 +1069,21 @@ bool ImGui::ImageButtonEx(ImGuiID id, ImTextureID texture_id, const ImVec2& size return pressed; } -// frame_padding < 0: uses FramePadding from style (default) -// frame_padding = 0: no framing -// frame_padding > 0: set framing size +bool ImGui::ImageButton(const char* str_id, ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, const ImVec4& bg_col, const ImVec4& tint_col) +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->SkipItems) + return false; + + return ImageButtonEx(window->GetID(str_id), user_texture_id, size, uv0, uv1, bg_col, tint_col); +} + +#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS +// Legacy API obsoleted in 1.89. Two differences with new ImageButton() +// - new ImageButton() requires an explicit 'const char* str_id' Old ImageButton() used opaque imTextureId (created issue with: multiple buttons with same image, transient texture id values, opaque computation of ID) +// - new ImageButton() always use style.FramePadding Old ImageButton() had an override argument. +// If you need to change padding with new ImageButton() you can use PushStyleVar(ImGuiStyleVar_FramePadding, value), consistent with other Button functions. bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const ImVec2& uv0, const ImVec2& uv1, int frame_padding, const ImVec4& bg_col, const ImVec4& tint_col) { ImGuiContext& g = *GImGui; @@ -1071,9 +1096,14 @@ bool ImGui::ImageButton(ImTextureID user_texture_id, const ImVec2& size, const I const ImGuiID id = window->GetID("#image"); PopID(); - const ImVec2 padding = (frame_padding >= 0) ? ImVec2((float)frame_padding, (float)frame_padding) : g.Style.FramePadding; - return ImageButtonEx(id, user_texture_id, size, uv0, uv1, padding, bg_col, tint_col); + if (frame_padding >= 0) + PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2((float)frame_padding, (float)frame_padding)); + bool ret = ImageButtonEx(id, user_texture_id, size, uv0, uv1, bg_col, tint_col); + if (frame_padding >= 0) + PopStyleVar(); + return ret; } +#endif // #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS bool ImGui::Checkbox(const char* label, bool* v) { @@ -1447,16 +1477,12 @@ void ImGui::Separator() } // Using 'hover_visibility_delay' allows us to hide the highlight and mouse cursor for a short time, which can be convenient to reduce visual noise. -bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend, float hover_visibility_delay) +bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float* size1, float* size2, float min_size1, float min_size2, float hover_extend, float hover_visibility_delay, ImU32 bg_col) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - const ImGuiItemFlags item_flags_backup = g.CurrentItemFlags; - g.CurrentItemFlags |= ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus; - bool item_add = ItemAdd(bb, id); - g.CurrentItemFlags = item_flags_backup; - if (!item_add) + if (!ItemAdd(bb, id, NULL, ImGuiItemFlags_NoNav)) return false; bool hovered, held; @@ -1499,7 +1525,9 @@ bool ImGui::SplitterBehavior(const ImRect& bb, ImGuiID id, ImGuiAxis axis, float } } - // Render + // Render at new position + if (bg_col & IM_COL32_A_MASK) + window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, bg_col, 0.0f); const ImU32 col = GetColorU32(held ? ImGuiCol_SeparatorActive : (hovered && g.HoveredIdTimer >= hover_visibility_delay) ? ImGuiCol_SeparatorHovered : ImGuiCol_Separator); window->DrawList->AddRectFilled(bb_render.Min, bb_render.Max, col, 0.0f); @@ -1550,12 +1578,12 @@ void ImGui::ShrinkWidths(ImGuiShrinkWidthItem* items, int count, float width_exc items[n].Width = width_rounded; } while (width_excess > 0.0f) - for (int n = 0; n < count; n++) - if (items[n].Width + 1.0f <= items[n].InitialWidth) - { - items[n].Width += 1.0f; - width_excess -= 1.0f; - } + for (int n = 0; n < count && width_excess > 0.0f; n++) + { + float width_to_add = ImMin(items[n].InitialWidth - items[n].Width, 1.0f); + items[n].Width += width_to_add; + width_excess -= width_to_add; + } } //------------------------------------------------------------------------- @@ -1725,7 +1753,7 @@ bool ImGui::BeginComboPreview() ImGuiWindow* window = g.CurrentWindow; ImGuiComboPreviewData* preview_data = &g.ComboPreviewData; - if (window->SkipItems || !window->ClipRect.Overlaps(g.LastItemData.Rect)) // FIXME: Because we don't have a ImGuiItemStatusFlags_Visible flag to test last ItemAdd() result + if (window->SkipItems || !(g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible)) return false; IM_ASSERT(g.LastItemData.Rect.Min.x == preview_data->PreviewRect.Min.x && g.LastItemData.Rect.Min.y == preview_data->PreviewRect.Min.y); // Didn't call after BeginCombo/EndCombo block or forgot to pass ImGuiComboFlags_CustomPreview flag? if (!window->ClipRect.Contains(preview_data->PreviewRect)) // Narrower test (optional) @@ -1869,11 +1897,11 @@ bool ImGui::Combo(const char* label, int* current_item, const char* items_separa //------------------------------------------------------------------------- // [SECTION] Data Type and Data Formatting Helpers [Internal] //------------------------------------------------------------------------- -// - PatchFormatStringFloatToInt() // - DataTypeGetInfo() // - DataTypeFormatString() // - DataTypeApplyOp() // - DataTypeApplyOpFromText() +// - DataTypeCompare() // - DataTypeClamp() // - GetMinimumStepAtDecimalPrecision // - RoundScalarWithFormat<>() @@ -1899,30 +1927,6 @@ static const ImGuiDataTypeInfo GDataTypeInfo[] = }; IM_STATIC_ASSERT(IM_ARRAYSIZE(GDataTypeInfo) == ImGuiDataType_COUNT); -// FIXME-LEGACY: Prior to 1.61 our DragInt() function internally used floats and because of this the compile-time default value for format was "%.0f". -// Even though we changed the compile-time default, we expect users to have carried %f around, which would break the display of DragInt() calls. -// To honor backward compatibility we are rewriting the format string, unless IMGUI_DISABLE_OBSOLETE_FUNCTIONS is enabled. What could possibly go wrong?! -static const char* PatchFormatStringFloatToInt(const char* fmt) -{ - if (fmt[0] == '%' && fmt[1] == '.' && fmt[2] == '0' && fmt[3] == 'f' && fmt[4] == 0) // Fast legacy path for "%.0f" which is expected to be the most common case. - return "%d"; - const char* fmt_start = ImParseFormatFindStart(fmt); // Find % (if any, and ignore %%) - const char* fmt_end = ImParseFormatFindEnd(fmt_start); // Find end of format specifier, which itself is an exercise of confidence/recklessness (because snprintf is dependent on libc or user). - if (fmt_end > fmt_start && fmt_end[-1] == 'f') - { -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - if (fmt_start == fmt && fmt_end[0] == 0) - return "%d"; - const char* tmp_format; - ImFormatStringToTempBuffer(&tmp_format, NULL, "%.*s%%d%s", (int)(fmt_start - fmt), fmt, fmt_end); // Honor leading and trailing decorations, but lose alignment/precision. - return tmp_format; -#else - IM_ASSERT(0 && "DragInt(): Invalid format string!"); // Old versions used a default parameter of "%.0f", please replace with e.g. "%d" -#endif - } - return fmt; -} - const ImGuiDataTypeInfo* ImGui::DataTypeGetInfo(ImGuiDataType data_type) { IM_ASSERT(data_type >= 0 && data_type < ImGuiDataType_COUNT); @@ -2181,7 +2185,10 @@ bool ImGui::DragBehaviorT(ImGuiDataType data_type, TYPE* v, float v_speed, const else if (g.ActiveIdSource == ImGuiInputSource_Nav) { const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0; - adjust_delta = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiNavReadMode_RepeatFast, 1.0f / 10.0f, 10.0f)[axis]; + const bool tweak_slow = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakSlow : ImGuiKey_NavKeyboardTweakSlow); + const bool tweak_fast = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakFast : ImGuiKey_NavKeyboardTweakFast); + const float tweak_factor = tweak_slow ? 1.0f / 1.0f : tweak_fast ? 10.0f : 1.0f; + adjust_delta = GetNavTweakPressedAmount(axis) * tweak_factor; v_speed = ImMax(v_speed, GetMinimumStepAtDecimalPrecision(decimal_precision)); } adjust_delta *= v_speed; @@ -2279,6 +2286,7 @@ bool ImGui::DragBehavior(ImGuiID id, ImGuiDataType data_type, void* p_v, float v ImGuiContext& g = *GImGui; if (g.ActiveId == id) { + // Those are the things we can do easily outside the DragBehaviorT<> template, saves code generation. if (g.ActiveIdSource == ImGuiInputSource_Mouse && !g.IO.MouseDown[0]) ClearActiveID(); else if (g.ActiveIdSource == ImGuiInputSource_Nav && g.NavActivatePressedId == id && !g.ActiveIdIsJustActivated) @@ -2332,8 +2340,6 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, // Default format string when passing NULL if (format == NULL) format = DataTypeGetInfo(data_type)->PrintFmt; - else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) // (FIXME-LEGACY: Patch old "%.0f" format string to use "%d", read function more details.) - format = PatchFormatStringFloatToInt(format); const bool hovered = ItemHoverable(frame_bb, id); bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); @@ -2341,9 +2347,11 @@ bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, { // Tabbing or CTRL-clicking on Drag turns it into an InputText const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; - const bool clicked = (hovered && g.IO.MouseClicked[0]); - const bool double_clicked = (hovered && g.IO.MouseClickedCount[0] == 2); + const bool clicked = hovered && IsMouseClicked(0, id); + const bool double_clicked = (hovered && g.IO.MouseClickedCount[0] == 2 && TestKeyOwner(ImGuiKey_MouseLeft, id)); const bool make_active = (input_requested_by_tabbing || clicked || double_clicked || g.NavActivateId == id || g.NavActivateInputId == id); + if (make_active && (clicked || double_clicked)) + SetKeyOwner(ImGuiKey_MouseLeft, id); if (make_active && temp_input_allowed) if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || double_clicked || g.NavActivateInputId == id) temp_input_is_active = true; @@ -2539,35 +2547,6 @@ bool ImGui::DragIntRange2(const char* label, int* v_current_min, int* v_current_ return value_changed; } -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - -// Obsolete versions with power parameter. See https://github.com/ocornut/imgui/issues/3361 for details. -bool ImGui::DragScalar(const char* label, ImGuiDataType data_type, void* p_data, float v_speed, const void* p_min, const void* p_max, const char* format, float power) -{ - ImGuiSliderFlags drag_flags = ImGuiSliderFlags_None; - if (power != 1.0f) - { - IM_ASSERT(power == 1.0f && "Call function with ImGuiSliderFlags_Logarithmic flags instead of using the old 'float power' function!"); - IM_ASSERT(p_min != NULL && p_max != NULL); // When using a power curve the drag needs to have known bounds - drag_flags |= ImGuiSliderFlags_Logarithmic; // Fallback for non-asserting paths - } - return DragScalar(label, data_type, p_data, v_speed, p_min, p_max, format, drag_flags); -} - -bool ImGui::DragScalarN(const char* label, ImGuiDataType data_type, void* p_data, int components, float v_speed, const void* p_min, const void* p_max, const char* format, float power) -{ - ImGuiSliderFlags drag_flags = ImGuiSliderFlags_None; - if (power != 1.0f) - { - IM_ASSERT(power == 1.0f && "Call function with ImGuiSliderFlags_Logarithmic flags instead of using the old 'float power' function!"); - IM_ASSERT(p_min != NULL && p_max != NULL); // When using a power curve the drag needs to have known bounds - drag_flags |= ImGuiSliderFlags_Logarithmic; // Fallback for non-asserting paths - } - return DragScalarN(label, data_type, p_data, components, v_speed, p_min, p_max, format, drag_flags); -} - -#endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS - //------------------------------------------------------------------------- // [SECTION] Widgets: SliderScalar, SliderFloat, SliderInt, etc. //------------------------------------------------------------------------- @@ -2787,25 +2766,26 @@ bool ImGui::SliderBehaviorT(const ImRect& bb, ImGuiID id, ImGuiDataType data_typ g.SliderCurrentAccumDirty = false; } - const ImVec2 input_delta2 = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard | ImGuiNavDirSourceFlags_PadDPad, ImGuiNavReadMode_RepeatFast, 0.0f, 0.0f); - float input_delta = (axis == ImGuiAxis_X) ? input_delta2.x : -input_delta2.y; + float input_delta = (axis == ImGuiAxis_X) ? GetNavTweakPressedAmount(axis) : -GetNavTweakPressedAmount(axis); if (input_delta != 0.0f) { + const bool tweak_slow = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakSlow : ImGuiKey_NavKeyboardTweakSlow); + const bool tweak_fast = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) ? ImGuiKey_NavGamepadTweakFast : ImGuiKey_NavKeyboardTweakFast); const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0; if (decimal_precision > 0) { input_delta /= 100.0f; // Gamepad/keyboard tweak speeds in % of slider bounds - if (IsNavInputDown(ImGuiNavInput_TweakSlow)) + if (tweak_slow) input_delta /= 10.0f; } else { - if ((v_range >= -100.0f && v_range <= 100.0f) || IsNavInputDown(ImGuiNavInput_TweakSlow)) + if ((v_range >= -100.0f && v_range <= 100.0f) || tweak_slow) input_delta = ((input_delta < 0.0f) ? -1.0f : +1.0f) / (float)v_range; // Gamepad/keyboard tweak speeds in integer steps else input_delta /= 100.0f; } - if (IsNavInputDown(ImGuiNavInput_TweakFast)) + if (tweak_fast) input_delta *= 10.0f; g.SliderCurrentAccum += input_delta; @@ -2893,6 +2873,7 @@ bool ImGui::SliderBehavior(const ImRect& bb, ImGuiID id, ImGuiDataType data_type // Read imgui.cpp "API BREAKING CHANGES" section for 1.78 if you hit this assert. IM_ASSERT((flags == 1 || (flags & ImGuiSliderFlags_InvalidMask_) == 0) && "Invalid ImGuiSliderFlags flag! Has the 'float power' argument been mistakenly cast to flags? Call function with ImGuiSliderFlags_Logarithmic flags instead."); + // Those are the things we can do easily outside the SliderBehaviorT<> template, saves code generation. ImGuiContext& g = *GImGui; if ((g.LastItemData.InFlags & ImGuiItemFlags_ReadOnly) || (flags & ImGuiSliderFlags_ReadOnly)) return false; @@ -2952,8 +2933,6 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat // Default format string when passing NULL if (format == NULL) format = DataTypeGetInfo(data_type)->PrintFmt; - else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) // (FIXME-LEGACY: Patch old "%.0f" format string to use "%d", read function more details.) - format = PatchFormatStringFloatToInt(format); const bool hovered = ItemHoverable(frame_bb, id); bool temp_input_is_active = temp_input_allowed && TempInputIsActive(id); @@ -2961,8 +2940,10 @@ bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_dat { // Tabbing or CTRL-clicking on Slider turns it into an input box const bool input_requested_by_tabbing = temp_input_allowed && (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_FocusedByTabbing) != 0; - const bool clicked = (hovered && g.IO.MouseClicked[0]); + const bool clicked = hovered && IsMouseClicked(0, id); const bool make_active = (input_requested_by_tabbing || clicked || g.NavActivateId == id || g.NavActivateInputId == id); + if (make_active && clicked) + SetKeyOwner(ImGuiKey_MouseLeft, id); if (make_active && temp_input_allowed) if (input_requested_by_tabbing || (clicked && g.IO.KeyCtrl) || g.NavActivateInputId == id) temp_input_is_active = true; @@ -3119,12 +3100,13 @@ bool ImGui::VSliderScalar(const char* label, const ImVec2& size, ImGuiDataType d // Default format string when passing NULL if (format == NULL) format = DataTypeGetInfo(data_type)->PrintFmt; - else if (data_type == ImGuiDataType_S32 && strcmp(format, "%d") != 0) // (FIXME-LEGACY: Patch old "%.0f" format string to use "%d", read function more details.) - format = PatchFormatStringFloatToInt(format); const bool hovered = ItemHoverable(frame_bb, id); - if ((hovered && g.IO.MouseClicked[0]) || g.NavActivateId == id || g.NavActivateInputId == id) + const bool clicked = hovered && IsMouseClicked(0, id); + if (clicked || g.NavActivateId == id || g.NavActivateInputId == id) { + if (clicked) + SetKeyOwner(ImGuiKey_MouseLeft, id); SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); @@ -3167,33 +3149,6 @@ bool ImGui::VSliderInt(const char* label, const ImVec2& size, int* v, int v_min, return VSliderScalar(label, size, ImGuiDataType_S32, v, &v_min, &v_max, format, flags); } -#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS - -// Obsolete versions with power parameter. See https://github.com/ocornut/imgui/issues/3361 for details. -bool ImGui::SliderScalar(const char* label, ImGuiDataType data_type, void* p_data, const void* p_min, const void* p_max, const char* format, float power) -{ - ImGuiSliderFlags slider_flags = ImGuiSliderFlags_None; - if (power != 1.0f) - { - IM_ASSERT(power == 1.0f && "Call function with ImGuiSliderFlags_Logarithmic flags instead of using the old 'float power' function!"); - slider_flags |= ImGuiSliderFlags_Logarithmic; // Fallback for non-asserting paths - } - return SliderScalar(label, data_type, p_data, p_min, p_max, format, slider_flags); -} - -bool ImGui::SliderScalarN(const char* label, ImGuiDataType data_type, void* v, int components, const void* v_min, const void* v_max, const char* format, float power) -{ - ImGuiSliderFlags slider_flags = ImGuiSliderFlags_None; - if (power != 1.0f) - { - IM_ASSERT(power == 1.0f && "Call function with ImGuiSliderFlags_Logarithmic flags instead of using the old 'float power' function!"); - slider_flags |= ImGuiSliderFlags_Logarithmic; // Fallback for non-asserting paths - } - return SliderScalarN(label, data_type, v, components, v_min, v_max, format, slider_flags); -} - -#endif // IMGUI_DISABLE_OBSOLETE_FUNCTIONS - //------------------------------------------------------------------------- // [SECTION] Widgets: InputScalar, InputFloat, InputInt, etc. //------------------------------------------------------------------------- @@ -3441,6 +3396,7 @@ bool ImGui::InputScalar(const char* label, ImGuiDataType data_type, void* p_data SetNextItemWidth(ImMax(1.0f, CalcItemWidth() - (button_size + style.ItemInnerSpacing.x) * 2)); if (InputText("", buf, IM_ARRAYSIZE(buf), flags)) // PushId(label) + "" gives us the expected ID from outside point of view value_changed = DataTypeApplyFromText(buf, data_type, p_data, format); + IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags); // Step buttons const ImVec2 backup_frame_padding = style.FramePadding; @@ -3595,7 +3551,7 @@ bool ImGui::InputTextMultiline(const char* label, char* buf, size_t buf_size, co bool ImGui::InputTextWithHint(const char* label, const char* hint, char* buf, size_t buf_size, ImGuiInputTextFlags flags, ImGuiInputTextCallback callback, void* user_data) { - IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() + IM_ASSERT(!(flags & ImGuiInputTextFlags_Multiline)); // call InputTextMultiline() or InputTextEx() manually if you need multi-line + hint. return InputTextEx(label, hint, buf, (int)buf_size, ImVec2(0, 0), flags, callback, user_data); } @@ -3613,9 +3569,9 @@ static int InputTextCalcTextLenAndLineCount(const char* text_begin, const char** return line_count; } -static ImVec2 InputTextCalcTextSizeW(const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line) +static ImVec2 InputTextCalcTextSizeW(ImGuiContext* ctx, const ImWchar* text_begin, const ImWchar* text_end, const ImWchar** remaining, ImVec2* out_offset, bool stop_on_new_line) { - ImGuiContext& g = *GImGui; + ImGuiContext& g = *ctx; ImFont* font = g.Font; const float line_height = g.FontSize; const float scale = line_height / font->FontSize; @@ -3664,14 +3620,14 @@ namespace ImStb static int STB_TEXTEDIT_STRINGLEN(const ImGuiInputTextState* obj) { return obj->CurLenW; } static ImWchar STB_TEXTEDIT_GETCHAR(const ImGuiInputTextState* obj, int idx) { return obj->TextW[idx]; } -static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx + char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *GImGui; return g.Font->GetCharAdvance(c) * (g.FontSize / g.Font->FontSize); } +static float STB_TEXTEDIT_GETWIDTH(ImGuiInputTextState* obj, int line_start_idx, int char_idx) { ImWchar c = obj->TextW[line_start_idx + char_idx]; if (c == '\n') return STB_TEXTEDIT_GETWIDTH_NEWLINE; ImGuiContext& g = *obj->Ctx; return g.Font->GetCharAdvance(c) * (g.FontSize / g.Font->FontSize); } static int STB_TEXTEDIT_KEYTOTEXT(int key) { return key >= 0x200000 ? 0 : key; } static ImWchar STB_TEXTEDIT_NEWLINE = '\n'; static void STB_TEXTEDIT_LAYOUTROW(StbTexteditRow* r, ImGuiInputTextState* obj, int line_start_idx) { const ImWchar* text = obj->TextW.Data; const ImWchar* text_remaining = NULL; - const ImVec2 size = InputTextCalcTextSizeW(text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true); + const ImVec2 size = InputTextCalcTextSizeW(obj->Ctx, text + line_start_idx, text + obj->CurLenW, &text_remaining, NULL, true); r->x0 = 0.0f; r->x1 = size.x; r->baseline_y_delta = size.y; @@ -3686,13 +3642,10 @@ static int is_word_boundary_from_right(ImGuiInputTextState* obj, int idx) static int is_word_boundary_from_left(ImGuiInputTextState* obj, int idx) { if (obj->Flags & ImGuiInputTextFlags_Password) return 0; return idx > 0 ? (!is_separator(obj->TextW[idx - 1]) && is_separator(obj->TextW[idx])) : 1; } static int STB_TEXTEDIT_MOVEWORDLEFT_IMPL(ImGuiInputTextState* obj, int idx) { idx--; while (idx >= 0 && !is_word_boundary_from_right(obj, idx)) idx--; return idx < 0 ? 0 : idx; } static int STB_TEXTEDIT_MOVEWORDRIGHT_MAC(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_left(obj, idx)) idx++; return idx > len ? len : idx; } -#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h -#ifdef __APPLE__ // FIXME: Move setting to IO structure -#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_MAC -#else static int STB_TEXTEDIT_MOVEWORDRIGHT_WIN(ImGuiInputTextState* obj, int idx) { idx++; int len = obj->CurLenW; while (idx < len && !is_word_boundary_from_right(obj, idx)) idx++; return idx > len ? len : idx; } -#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_WIN -#endif +static int STB_TEXTEDIT_MOVEWORDRIGHT_IMPL(ImGuiInputTextState* obj, int idx) { ImGuiContext& g = *obj->Ctx; if (g.IO.ConfigMacOSXBehaviors) return STB_TEXTEDIT_MOVEWORDRIGHT_MAC(obj, idx); else return STB_TEXTEDIT_MOVEWORDRIGHT_WIN(obj, idx); } +#define STB_TEXTEDIT_MOVEWORDLEFT STB_TEXTEDIT_MOVEWORDLEFT_IMPL // They need to be #define for stb_textedit.h +#define STB_TEXTEDIT_MOVEWORDRIGHT STB_TEXTEDIT_MOVEWORDRIGHT_IMPL static void STB_TEXTEDIT_DELETECHARS(ImGuiInputTextState* obj, int pos, int n) { @@ -3770,11 +3723,12 @@ static void stb_textedit_replace(ImGuiInputTextState* str, STB_TexteditState* st { stb_text_makeundo_replace(str, state, 0, str->CurLenW, text_len); ImStb::STB_TEXTEDIT_DELETECHARS(str, 0, str->CurLenW); + state->cursor = state->select_start = state->select_end = 0; if (text_len <= 0) return; if (ImStb::STB_TEXTEDIT_INSERTCHARS(str, 0, text, text_len)) { - state->cursor = text_len; + state->cursor = state->select_start = state->select_end = text_len; state->has_preferred_x = 0; return; } @@ -3893,6 +3847,13 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f ImGuiContext& g = *GImGui; const unsigned c_decimal_point = (unsigned int)g.PlatformLocaleDecimalPoint; + // Full-width -> half-width conversion for numeric fields (https://en.wikipedia.org/wiki/Halfwidth_and_Fullwidth_Forms_(Unicode_block) + // While this is mostly convenient, this has the side-effect for uninformed users accidentally inputting full-width characters that they may + // scratch their head as to why it works in numerical fields vs in generic text fields it would require support in the font. + if (flags & (ImGuiInputTextFlags_CharsDecimal | ImGuiInputTextFlags_CharsScientific | ImGuiInputTextFlags_CharsHexadecimal)) + if (c >= 0xFF01 && c <= 0xFF5E) + c = c - 0xFF01 + 0x21; + // Allow 0-9 . - + * / if (flags & ImGuiInputTextFlags_CharsDecimal) if (!(c >= '0' && c <= '9') && (c != c_decimal_point) && (c != '-') && (c != '+') && (c != '*') && (c != '/')) @@ -3911,11 +3872,13 @@ static bool InputTextFilterCharacter(unsigned int* p_char, ImGuiInputTextFlags f // Turn a-z into A-Z if (flags & ImGuiInputTextFlags_CharsUppercase) if (c >= 'a' && c <= 'z') - *p_char = (c += (unsigned int)('A' - 'a')); + c += (unsigned int)('A' - 'a'); if (flags & ImGuiInputTextFlags_CharsNoBlank) if (ImCharIsBlankW(c)) return false; + + *p_char = c; } // Custom callback filter @@ -4139,24 +4102,24 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ SetActiveID(id, window); SetFocusID(id, window); FocusWindow(window); - - // Declare our inputs - IM_ASSERT(ImGuiNavInput_COUNT < 32); + } + if (g.ActiveId == id) + { + // Declare some inputs, the other are registered and polled via Shortcut() routing system. + if (user_clicked) + SetKeyOwner(ImGuiKey_MouseLeft, id); g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Left) | (1 << ImGuiDir_Right); if (is_multiline || (flags & ImGuiInputTextFlags_CallbackHistory)) g.ActiveIdUsingNavDirMask |= (1 << ImGuiDir_Up) | (1 << ImGuiDir_Down); - g.ActiveIdUsingNavInputMask |= (1 << ImGuiNavInput_Cancel); - SetActiveIdUsingKey(ImGuiKey_Home); - SetActiveIdUsingKey(ImGuiKey_End); + SetKeyOwner(ImGuiKey_Home, id); + SetKeyOwner(ImGuiKey_End, id); if (is_multiline) { - SetActiveIdUsingKey(ImGuiKey_PageUp); - SetActiveIdUsingKey(ImGuiKey_PageDown); + SetKeyOwner(ImGuiKey_PageUp, id); + SetKeyOwner(ImGuiKey_PageDown, id); } if (flags & (ImGuiInputTextFlags_CallbackCompletion | ImGuiInputTextFlags_AllowTabInput)) // Disable keyboard tabbing out as we will use the \t character. - { - SetActiveIdUsingKey(ImGuiKey_Tab); - } + SetKeyOwner(ImGuiKey_Tab, id); } // We have an edge case if ActiveId was set through another widget (e.g. widget being swapped), clear id immediately (don't wait until the end of the function) @@ -4168,10 +4131,10 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ clear_active_id = true; // Lock the decision of whether we are going to take the path displaying the cursor or selection - const bool render_cursor = (g.ActiveId == id) || (state && user_scroll_active); + bool render_cursor = (g.ActiveId == id) || (state && user_scroll_active); bool render_selection = state && (state->HasSelection() || select_all) && (RENDER_SELECTION_WHEN_INACTIVE || render_cursor); bool value_changed = false; - bool enter_pressed = false; + bool validated = false; // When read-only we always use the live data passed to the function // FIXME-OPT: Because our selection/cursor code currently needs the wide text we need to convert it when active, which is not ideal :( @@ -4267,10 +4230,12 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } else if (io.MouseClicked[0] && !state->SelectedAllMouseLock) { - // FIXME: unselect on late click could be done release? if (hovered) { - stb_textedit_click(state, &state->Stb, mouse_x, mouse_y); + if (io.KeyShift) + stb_textedit_drag(state, &state->Stb, mouse_x, mouse_y); + else + stb_textedit_click(state, &state->Stb, mouse_x, mouse_y); state->CursorAnimReset(); } } @@ -4283,7 +4248,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (state->SelectedAllMouseLock && !io.MouseDown[0]) state->SelectedAllMouseLock = false; - // We except backends to emit a Tab key but some also emit a Tab character which we ignore (#2467, #1336) + // We expect backends to emit a Tab key but some also emit a Tab character which we ignore (#2467, #1336) // (For Tab and Enter: Win32/SFML/Allegro are sending both keys and chars, GLFW and SDL are only sending keys. For Space they all send all threes) const bool ignore_char_inputs = (io.KeyCtrl && !io.KeyAlt) || (is_osx && io.KeySuper); if ((flags & ImGuiInputTextFlags_AllowTabInput) && IsKeyPressed(ImGuiKey_Tab) && !ignore_char_inputs && !io.KeyShift && !is_readonly) @@ -4314,7 +4279,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } // Process other shortcuts/key-presses - bool cancel_edit = false; + bool revert_edit = false; if (g.ActiveId == id && !g.ActiveIdIsJustActivated && !clear_active_id) { IM_ASSERT(state != NULL); @@ -4324,24 +4289,26 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ const int k_mask = (io.KeyShift ? STB_TEXTEDIT_K_SHIFT : 0); const bool is_osx = io.ConfigMacOSXBehaviors; - const bool is_osx_shift_shortcut = is_osx && (io.KeyMods == (ImGuiModFlags_Super | ImGuiModFlags_Shift)); const bool is_wordmove_key_down = is_osx ? io.KeyAlt : io.KeyCtrl; // OS X style: Text editing cursor movement using Alt instead of Ctrl const bool is_startend_key_down = is_osx && io.KeySuper && !io.KeyCtrl && !io.KeyAlt; // OS X style: Line/Text Start and End using Cmd+Arrows instead of Home/End - const bool is_ctrl_key_only = (io.KeyMods == ImGuiModFlags_Ctrl); - const bool is_shift_key_only = (io.KeyMods == ImGuiModFlags_Shift); - const bool is_shortcut_key = g.IO.ConfigMacOSXBehaviors ? (io.KeyMods == ImGuiModFlags_Super) : (io.KeyMods == ImGuiModFlags_Ctrl); - const bool is_cut = ((is_shortcut_key && IsKeyPressed(ImGuiKey_X)) || (is_shift_key_only && IsKeyPressed(ImGuiKey_Delete))) && !is_readonly && !is_password && (!is_multiline || state->HasSelection()); - const bool is_copy = ((is_shortcut_key && IsKeyPressed(ImGuiKey_C)) || (is_ctrl_key_only && IsKeyPressed(ImGuiKey_Insert))) && !is_password && (!is_multiline || state->HasSelection()); - const bool is_paste = ((is_shortcut_key && IsKeyPressed(ImGuiKey_V)) || (is_shift_key_only && IsKeyPressed(ImGuiKey_Insert))) && !is_readonly; - const bool is_undo = ((is_shortcut_key && IsKeyPressed(ImGuiKey_Z)) && !is_readonly && is_undoable); - const bool is_redo = ((is_shortcut_key && IsKeyPressed(ImGuiKey_Y)) || (is_osx_shift_shortcut && IsKeyPressed(ImGuiKey_Z))) && !is_readonly && is_undoable; + // Using Shortcut() with ImGuiInputFlags_RouteFocused (default policy) to allow routing operations for other code (e.g. calling window trying to use CTRL+A and CTRL+B: formet would be handled by InputText) + // Otherwise we could simply assume that we own the keys as we are active. + const ImGuiInputFlags f_repeat = ImGuiInputFlags_Repeat; + const bool is_cut = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_X, id, f_repeat) || Shortcut(ImGuiMod_Shift | ImGuiKey_Delete, id, f_repeat)) && !is_readonly && !is_password && (!is_multiline || state->HasSelection()); + const bool is_copy = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_C, id) || Shortcut(ImGuiMod_Ctrl | ImGuiKey_Insert, id)) && !is_password && (!is_multiline || state->HasSelection()); + const bool is_paste = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_V, id, f_repeat) || Shortcut(ImGuiMod_Shift | ImGuiKey_Insert, id, f_repeat)) && !is_readonly; + const bool is_undo = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_Z, id, f_repeat)) && !is_readonly && is_undoable; + const bool is_redo = (Shortcut(ImGuiMod_Shortcut | ImGuiKey_Y, id, f_repeat) || (is_osx && Shortcut(ImGuiMod_Shortcut | ImGuiMod_Shift | ImGuiKey_Z, id, f_repeat))) && !is_readonly && is_undoable; + const bool is_select_all = Shortcut(ImGuiMod_Shortcut | ImGuiKey_A, id); // We allow validate/cancel with Nav source (gamepad) to makes it easier to undo an accidental NavInput press with no keyboard wired, but otherwise it isn't very useful. - const bool is_validate_enter = IsKeyPressed(ImGuiKey_Enter) || IsKeyPressed(ImGuiKey_KeypadEnter); - const bool is_validate_nav = (IsNavInputTest(ImGuiNavInput_Activate, ImGuiNavReadMode_Pressed) && !IsKeyPressed(ImGuiKey_Space)) || IsNavInputTest(ImGuiNavInput_Input, ImGuiNavReadMode_Pressed); - const bool is_cancel = IsKeyPressed(ImGuiKey_Escape) || IsNavInputTest(ImGuiNavInput_Cancel, ImGuiNavReadMode_Pressed); + const bool nav_gamepad_active = (io.ConfigFlags & ImGuiConfigFlags_NavEnableGamepad) != 0 && (io.BackendFlags & ImGuiBackendFlags_HasGamepad) != 0; + const bool is_enter_pressed = IsKeyPressed(ImGuiKey_Enter, true) || IsKeyPressed(ImGuiKey_KeypadEnter, true); + const bool is_gamepad_validate = nav_gamepad_active && (IsKeyPressed(ImGuiKey_NavGamepadActivate, false) || IsKeyPressed(ImGuiKey_NavGamepadInput, false)); + const bool is_cancel = Shortcut(ImGuiKey_Escape, id, f_repeat) || (nav_gamepad_active && Shortcut(ImGuiKey_NavGamepadCancel, id, f_repeat)); + // FIXME: Should use more Shortcut() and reduce IsKeyPressed()+SetKeyOwner(), but requires modifiers combination to be taken account of. if (IsKeyPressed(ImGuiKey_LeftArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINESTART : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDLEFT : STB_TEXTEDIT_K_LEFT) | k_mask); } else if (IsKeyPressed(ImGuiKey_RightArrow)) { state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_LINEEND : is_wordmove_key_down ? STB_TEXTEDIT_K_WORDRIGHT : STB_TEXTEDIT_K_RIGHT) | k_mask); } else if (IsKeyPressed(ImGuiKey_UpArrow) && is_multiline) { if (io.KeyCtrl) SetScrollY(draw_window, ImMax(draw_window->Scroll.y - g.FontSize, 0.0f)); else state->OnKeyPressed((is_startend_key_down ? STB_TEXTEDIT_K_TEXTSTART : STB_TEXTEDIT_K_UP) | k_mask); } @@ -4362,12 +4329,17 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } state->OnKeyPressed(STB_TEXTEDIT_K_BACKSPACE | k_mask); } - else if (is_validate_enter) + else if (is_enter_pressed || is_gamepad_validate) { + // Determine if we turn Enter into a \n character bool ctrl_enter_for_new_line = (flags & ImGuiInputTextFlags_CtrlEnterForNewLine) != 0; - if (!is_multiline || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl)) + if (!is_multiline || is_gamepad_validate || (ctrl_enter_for_new_line && !io.KeyCtrl) || (!ctrl_enter_for_new_line && io.KeyCtrl)) { - enter_pressed = clear_active_id = true; + validated = true; + if (io.ConfigInputTextEnterKeepActive && !is_multiline) + state->SelectAll(); // No need to scroll + else + clear_active_id = true; } else if (!is_readonly) { @@ -4376,21 +4348,32 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ state->OnKeyPressed((int)c); } } - else if (is_validate_nav) - { - IM_ASSERT(!is_validate_enter); - enter_pressed = clear_active_id = true; - } else if (is_cancel) { - clear_active_id = cancel_edit = true; + if (flags & ImGuiInputTextFlags_EscapeClearsAll) + { + if (state->CurLenA > 0) + { + revert_edit = true; + } + else + { + render_cursor = render_selection = false; + clear_active_id = true; + } + } + else + { + clear_active_id = revert_edit = true; + render_cursor = render_selection = false; + } } else if (is_undo || is_redo) { state->OnKeyPressed(is_undo ? STB_TEXTEDIT_K_UNDO : STB_TEXTEDIT_K_REDO); state->ClearSelection(); } - else if (is_shortcut_key && IsKeyPressed(ImGuiKey_A)) + else if (is_select_all) { state->SelectAll(); state->CursorFollow = true; @@ -4454,11 +4437,19 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (g.ActiveId == id) { IM_ASSERT(state != NULL); - if (cancel_edit) + if (revert_edit && !is_readonly) { - // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents. - if (!is_readonly && strcmp(buf, state->InitialTextA.Data) != 0) + if (flags & ImGuiInputTextFlags_EscapeClearsAll) { + // Clear input + apply_new_text = ""; + apply_new_text_length = 0; + STB_TEXTEDIT_CHARTYPE empty_string; + stb_textedit_replace(state, &state->Stb, &empty_string, 0); + } + else if (strcmp(buf, state->InitialTextA.Data) != 0) + { + // Restore initial value. Only return true if restoring to the initial value changes the current buffer contents. // Push records into the undo stack so we can CTRL+Z the revert operation itself apply_new_text = state->InitialTextA.Data; apply_new_text_length = state->InitialTextA.Size - 1; @@ -4483,7 +4474,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // When using 'ImGuiInputTextFlags_EnterReturnsTrue' as a special case we reapply the live buffer back to the input buffer before clearing ActiveId, even though strictly speaking it wasn't modified on this frame. // If we didn't do that, code like InputInt() with ImGuiInputTextFlags_EnterReturnsTrue would fail. // This also allows the user to use InputText() with ImGuiInputTextFlags_EnterReturnsTrue without maintaining any user-side storage (please note that if you use this property along ImGuiInputTextFlags_CallbackResize you can end up with your temporary string object unnecessarily allocating once a frame, either store your string data, either if you don't then don't use ImGuiInputTextFlags_CallbackResize). - const bool apply_edit_back_to_user_buffer = !cancel_edit || (enter_pressed && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0); + const bool apply_edit_back_to_user_buffer = !revert_edit || (validated && (flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0); if (apply_edit_back_to_user_buffer) { // Apply new value immediately - copy modified buffer back @@ -4558,6 +4549,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (callback_data.SelectionEnd != utf8_selection_end || buf_dirty) { state->Stb.select_end = (callback_data.SelectionEnd == callback_data.SelectionStart) ? state->Stb.select_start : ImTextCountCharsFromUtf8(callback_data.Buf, callback_data.Buf + callback_data.SelectionEnd); } if (buf_dirty) { + IM_ASSERT((flags & ImGuiInputTextFlags_ReadOnly) == 0); IM_ASSERT(callback_data.BufTextLen == (int)strlen(callback_data.Buf)); // You need to maintain BufTextLen if you change the text! InputTextReconcileUndoStateAfterUserCallback(state, callback_data.Buf, callback_data.BufTextLen); // FIXME: Move the rest of this block inside function and rename to InputTextReconcileStateAfterUserCallback() ? if (callback_data.BufTextLen > backup_current_text_length && is_resizable) @@ -4576,9 +4568,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ apply_new_text_length = state->CurLenA; } } - - // Clear temporary user storage - state->Flags = ImGuiInputTextFlags_None; } // Copy result to user buffer. This can currently only happen when (g.ActiveId == id) @@ -4692,11 +4681,11 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ searches_result_line_no[1] = line_count; // Calculate 2d position by finding the beginning of the line and measuring distance - cursor_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x; + cursor_offset.x = InputTextCalcTextSizeW(&g, ImStrbolW(searches_input_ptr[0], text_begin), searches_input_ptr[0]).x; cursor_offset.y = searches_result_line_no[0] * g.FontSize; if (searches_result_line_no[1] >= 0) { - select_start_offset.x = InputTextCalcTextSizeW(ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x; + select_start_offset.x = InputTextCalcTextSizeW(&g, ImStrbolW(searches_input_ptr[1], text_begin), searches_input_ptr[1]).x; select_start_offset.y = searches_result_line_no[1] * g.FontSize; } @@ -4765,7 +4754,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ } else { - ImVec2 rect_size = InputTextCalcTextSizeW(p, text_selected_end, &p, NULL, true); + ImVec2 rect_size = InputTextCalcTextSizeW(&g, p, text_selected_end, &p, NULL, true); if (rect_size.x <= 0.0f) rect_size.x = IM_FLOOR(g.Font->GetCharAdvance((ImWchar)' ') * 0.50f); // So we can see selected empty lines ImRect rect(rect_pos + ImVec2(0.0f, bg_offy_up - g.FontSize), rect_pos + ImVec2(rect_size.x, bg_offy_dn)); rect.ClipWith(clip_rect); @@ -4859,7 +4848,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); if ((flags & ImGuiInputTextFlags_EnterReturnsTrue) != 0) - return enter_pressed; + return validated; else return value_changed; } @@ -4871,7 +4860,9 @@ void ImGui::DebugNodeInputTextState(ImGuiInputTextState* state) ImStb::STB_TexteditState* stb_state = &state->Stb; ImStb::StbUndoState* undo_state = &stb_state->undostate; Text("ID: 0x%08X, ActiveID: 0x%08X", state->ID, g.ActiveId); + DebugLocateItemOnHover(state->ID); Text("CurLenW: %d, CurLenA: %d, Cursor: %d, Selection: %d..%d", state->CurLenA, state->CurLenW, stb_state->cursor, stb_state->select_start, stb_state->select_end); + Text("has_preferred_x: %d (%.2f)", stb_state->has_preferred_x, stb_state->preferred_x); Text("undo_point: %d, redo_point: %d, undo_char_point: %d, redo_char_point: %d", undo_state->undo_point, undo_state->redo_point, undo_state->undo_char_point, undo_state->redo_char_point); if (BeginChild("undopoints", ImVec2(0.0f, GetTextLineHeight() * 15), true)) // Visualize undo state { @@ -5100,23 +5091,29 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag if (BeginPopup("picker")) { - picker_active_window = g.CurrentWindow; - if (label != label_display_end) + if (g.CurrentWindow->BeginCount == 1) { - TextEx(label, label_display_end); - Spacing(); + picker_active_window = g.CurrentWindow; + if (label != label_display_end) + { + TextEx(label, label_display_end); + Spacing(); + } + ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar; + ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf; + SetNextItemWidth(square_sz * 12.0f); // Use 256 + bar sizes? + value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x); } - ImGuiColorEditFlags picker_flags_to_forward = ImGuiColorEditFlags_DataTypeMask_ | ImGuiColorEditFlags_PickerMask_ | ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_HDR | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaBar; - ImGuiColorEditFlags picker_flags = (flags_untouched & picker_flags_to_forward) | ImGuiColorEditFlags_DisplayMask_ | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_AlphaPreviewHalf; - SetNextItemWidth(square_sz * 12.0f); // Use 256 + bar sizes? - value_changed |= ColorPicker4("##picker", col, picker_flags, &g.ColorPickerRef.x); EndPopup(); } } if (label != label_display_end && !(flags & ImGuiColorEditFlags_NoLabel)) { + // Position not necessarily next to last submitted button (e.g. if style.ColorButtonPosition == ImGuiDir_Left), + // but we need to use SameLine() to setup baseline correctly. Might want to refactor SameLine() to simplify this. SameLine(0.0f, style.ItemInnerSpacing.x); + window->DC.CursorPos.x = pos.x + ((flags & ImGuiColorEditFlags_NoInputs) ? w_button : w_full + style.ItemInnerSpacing.x); TextEx(label, label_display_end); } @@ -5153,7 +5150,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag bool accepted_drag_drop = false; if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_3F)) { - memcpy((float*)col, payload->Data, sizeof(float) * 3); // Preserve alpha if any //-V512 + memcpy((float*)col, payload->Data, sizeof(float) * 3); // Preserve alpha if any //-V512 //-V1086 value_changed = accepted_drag_drop = true; } if (const ImGuiPayload* payload = AcceptDragDropPayload(IMGUI_PAYLOAD_TYPE_COLOR_4F)) @@ -5172,7 +5169,7 @@ bool ImGui::ColorEdit4(const char* label, float col[4], ImGuiColorEditFlags flag if (picker_active_window && g.ActiveId != 0 && g.ActiveIdWindow == picker_active_window) g.LastItemData.ID = g.ActiveId; - if (value_changed) + if (value_changed && g.LastItemData.ID != 0) // In case of ID collision, the second EndGroup() won't catch g.ActiveId MarkItemEdited(g.LastItemData.ID); return value_changed; @@ -5560,7 +5557,7 @@ bool ImGui::ColorPicker4(const char* label, float col[4], ImGuiColorEditFlags fl if (value_changed && memcmp(backup_initial_col, col, components * sizeof(float)) == 0) value_changed = false; - if (value_changed) + if (value_changed && g.LastItemData.ID != 0) // In case of ID collision, the second EndGroup() won't catch g.ActiveId MarkItemEdited(g.LastItemData.ID); PopID(); @@ -5895,7 +5892,14 @@ bool ImGui::TreeNodeExV(const void* ptr_id, ImGuiTreeNodeFlags flags, const char return TreeNodeBehavior(window->GetID(ptr_id), flags, label, label_end); } -bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags) +void ImGui::TreeNodeSetOpen(ImGuiID id, bool open) +{ + ImGuiContext& g = *GImGui; + ImGuiStorage* storage = g.CurrentWindow->DC.StateStorage; + storage->SetInt(id, open ? 1 : 0); +} + +bool ImGui::TreeNodeUpdateNextOpen(ImGuiID id, ImGuiTreeNodeFlags flags) { if (flags & ImGuiTreeNodeFlags_Leaf) return true; @@ -5911,7 +5915,7 @@ bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags) if (g.NextItemData.OpenCond & ImGuiCond_Always) { is_open = g.NextItemData.OpenVal; - storage->SetInt(id, is_open); + TreeNodeSetOpen(id, is_open); } else { @@ -5920,7 +5924,7 @@ bool ImGui::TreeNodeBehaviorIsOpen(ImGuiID id, ImGuiTreeNodeFlags flags) if (stored_value == -1) { is_open = g.NextItemData.OpenVal; - storage->SetInt(id, is_open); + TreeNodeSetOpen(id, is_open); } else { @@ -5986,7 +5990,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l // For this purpose we essentially compare if g.NavIdIsAlive went from 0 to 1 between TreeNode() and TreePop(). // This is currently only support 32 level deep and we are fine with (1 << Depth) overflowing into a zero. const bool is_leaf = (flags & ImGuiTreeNodeFlags_Leaf) != 0; - bool is_open = TreeNodeBehaviorIsOpen(id, flags); + bool is_open = TreeNodeUpdateNextOpen(id, flags); if (is_open && !g.NavIdIsAlive && (flags & ImGuiTreeNodeFlags_NavLeftJumpsBackHere) && !(flags & ImGuiTreeNodeFlags_NoTreePushOnOpen)) window->DC.TreeJumpToParentOnPopMask |= (1 << window->DC.TreeDepth); @@ -6329,6 +6333,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl // We use NoHoldingActiveID on menus so user can click and _hold_ on a menu then drag to browse child entries ImGuiButtonFlags button_flags = 0; if (flags & ImGuiSelectableFlags_NoHoldingActiveID) { button_flags |= ImGuiButtonFlags_NoHoldingActiveId; } + if (flags & ImGuiSelectableFlags_NoSetKeyOwner) { button_flags |= ImGuiButtonFlags_NoSetKeyOwner; } if (flags & ImGuiSelectableFlags_SelectOnClick) { button_flags |= ImGuiButtonFlags_PressedOnClick; } if (flags & ImGuiSelectableFlags_SelectOnRelease) { button_flags |= ImGuiButtonFlags_PressedOnRelease; } if (flags & ImGuiSelectableFlags_AllowDoubleClick) { button_flags |= ImGuiButtonFlags_PressedOnClickRelease | ImGuiButtonFlags_PressedOnDoubleClick; } @@ -6345,7 +6350,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl // - (1) it would require focus scope to be set, need exposing PushFocusScope() or equivalent (e.g. BeginSelection() calling PushFocusScope()) // - (2) usage will fail with clipped items // The multi-select API aim to fix those issues, e.g. may be replaced with a BeginSelection() API. - if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == window->DC.NavFocusScopeIdCurrent) + if ((flags & ImGuiSelectableFlags_SelectOnNav) && g.NavJustMovedToId != 0 && g.NavJustMovedToFocusScopeId == g.CurrentFocusScopeId) if (g.NavJustMovedToId == id) selected = pressed = true; @@ -6354,7 +6359,7 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl { if (!g.NavDisableMouseHover && g.NavWindow == window && g.NavLayer == window->DC.NavLayerCurrent) { - SetNavID(id, window->DC.NavLayerCurrent, window->DC.NavFocusScopeIdCurrent, WindowRectAbsToRel(window, bb)); // (bb == NavRect) + SetNavID(id, window->DC.NavLayerCurrent, g.CurrentFocusScopeId, WindowRectAbsToRel(window, bb)); // (bb == NavRect) g.NavDisableHighlight = true; } } @@ -6369,8 +6374,6 @@ bool ImGui::Selectable(const char* label, bool selected, ImGuiSelectableFlags fl g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_ToggledSelection; // Render - if (held && (flags & ImGuiSelectableFlags_DrawHoveredWhenHeld)) - hovered = true; if (hovered || selected) { const ImU32 col = GetColorU32((held && hovered) ? ImGuiCol_HeaderActive : hovered ? ImGuiCol_HeaderHovered : ImGuiCol_Header); @@ -6845,7 +6848,7 @@ void ImGui::EndMenuBar() // To do so we claim focus back, restore NavId and then process the movement request for yet another frame. // This involve a one-frame delay which isn't very problematic in this situation. We could remove it by scoring in advance for multiple window (probably not worth bothering) const ImGuiNavLayer layer = ImGuiNavLayer_Menu; - IM_ASSERT(window->DC.NavLayersActiveMaskNext & (1 << layer)); // Sanity check + IM_ASSERT(window->DC.NavLayersActiveMaskNext & (1 << layer)); // Sanity check (FIXME: Seems unnecessary) FocusWindow(window); SetNavID(window->NavLastIds[layer], layer, 0, window->NavRectRel[layer]); g.NavDisableHighlight = true; // Hide highlight for the current frame so we don't see the intermediary selection. @@ -6975,9 +6978,9 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) // Sub-menus are ChildWindow so that mouse can be hovering across them (otherwise top-most popup menu would steal focus and not allow hovering on parent menu) // The first menu in a hierarchy isn't so hovering doesn't get across (otherwise e.g. resizing borders with ImGuiButtonFlags_FlattenChildren would react), but top-most BeginMenu() will bypass that limitation. - ImGuiWindowFlags flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; + ImGuiWindowFlags window_flags = ImGuiWindowFlags_ChildMenu | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoNavFocus; if (window->Flags & ImGuiWindowFlags_ChildMenu) - flags |= ImGuiWindowFlags_ChildWindow; + window_flags |= ImGuiWindowFlags_ChildWindow; // If a menu with same the ID was already submitted, we will append to it, matching the behavior of Begin(). // We are relying on a O(N) search - so O(N log N) over the frame - which seems like the most efficient for the expected small amount of BeginMenu() calls per frame. @@ -6985,7 +6988,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) if (g.MenusIdSubmittedThisFrame.contains(id)) { if (menu_is_open) - menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) + menu_is_open = BeginPopupEx(id, window_flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) else g.NextWindowData.ClearFlags(); // we behave like Begin() and need to consume those values return menu_is_open; @@ -6997,10 +7000,10 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) ImVec2 label_size = CalcTextSize(label, NULL, true); // Odd hack to allow hovering across menus of a same menu-set (otherwise we wouldn't be able to hover parent without always being a Child window) + // This is only done for items for the menu set and not the full parent window. const bool menuset_is_open = IsRootOfOpenMenuSet(); - ImGuiWindow* backed_nav_window = g.NavWindow; if (menuset_is_open) - g.NavWindow = window; + PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck, true); // The reference position stored in popup_pos will be used by Begin() to find a suitable position for the child menu, // However the final position is going to be different! It is chosen by FindBestWindowPosForPopup(). @@ -7011,7 +7014,9 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) BeginDisabled(); const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; bool pressed; - const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups; + + // We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another. + const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_NoHoldingActiveID | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SelectOnClick | ImGuiSelectableFlags_DontClosePopups; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { // Menu inside an horizontal menu bar @@ -7049,7 +7054,7 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) const bool hovered = (g.HoveredId == id) && enabled && !g.NavDisableMouseHover; if (menuset_is_open) - g.NavWindow = backed_nav_window; + PopItemFlag(); bool want_open = false; bool want_close = false; @@ -7058,26 +7063,30 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) // Close menu when not hovering it anymore unless we are moving roughly in the direction of the menu // Implement http://bjk5.com/post/44698559168/breaking-down-amazons-mega-dropdown to avoid using timers, so menus feels more reactive. bool moving_toward_child_menu = false; - ImGuiWindow* child_menu_window = (g.BeginPopupStack.Size < g.OpenPopupStack.Size && g.OpenPopupStack[g.BeginPopupStack.Size].SourceWindow == window) ? g.OpenPopupStack[g.BeginPopupStack.Size].Window : NULL; - if (g.HoveredWindow == window && child_menu_window != NULL && !(window->Flags & ImGuiWindowFlags_MenuBar)) + ImGuiPopupData* child_popup = (g.BeginPopupStack.Size < g.OpenPopupStack.Size) ? &g.OpenPopupStack[g.BeginPopupStack.Size] : NULL; // Popup candidate (testing below) + ImGuiWindow* child_menu_window = (child_popup && child_popup->Window && child_popup->Window->ParentWindow == window) ? child_popup->Window : NULL; + if (g.HoveredWindow == window && child_menu_window != NULL) { float ref_unit = g.FontSize; // FIXME-DPI + float child_dir = (window->Pos.x < child_menu_window->Pos.x) ? 1.0f : -1.0f; ImRect next_window_rect = child_menu_window->Rect(); ImVec2 ta = (g.IO.MousePos - g.IO.MouseDelta); - ImVec2 tb = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetTL() : next_window_rect.GetTR(); - ImVec2 tc = (window->Pos.x < child_menu_window->Pos.x) ? next_window_rect.GetBL() : next_window_rect.GetBR(); + ImVec2 tb = (child_dir > 0.0f) ? next_window_rect.GetTL() : next_window_rect.GetTR(); + ImVec2 tc = (child_dir > 0.0f) ? next_window_rect.GetBL() : next_window_rect.GetBR(); float extra = ImClamp(ImFabs(ta.x - tb.x) * 0.30f, ref_unit * 0.5f, ref_unit * 2.5f); // add a bit of extra slack. - ta.x += (window->Pos.x < child_menu_window->Pos.x) ? -0.5f : +0.5f; // to avoid numerical issues (FIXME: ??) + ta.x += child_dir * -0.5f; + tb.x += child_dir * ref_unit; + tc.x += child_dir * ref_unit; tb.y = ta.y + ImMax((tb.y - extra) - ta.y, -ref_unit * 8.0f); // triangle has maximum height to limit the slope and the bias toward large sub-menus tc.y = ta.y + ImMin((tc.y + extra) - ta.y, +ref_unit * 8.0f); moving_toward_child_menu = ImTriangleContainsPoint(ta, tb, tc, g.IO.MousePos); - //GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_toward_other_child_menu ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG] + //GetForegroundDrawList()->AddTriangleFilled(ta, tb, tc, moving_toward_child_menu ? IM_COL32(0,128,0,128) : IM_COL32(128,0,0,128)); // [DEBUG] } // The 'HovereWindow == window' check creates an inconsistency (e.g. moving away from menu slowly tends to hit same window, whereas moving away fast does not) // But we also need to not close the top-menu menu when moving over void. Perhaps we should extend the triangle check to a larger polygon. // (Remember to test this on BeginPopup("A")->BeginMenu("B") sequence which behaves slightly differently as B isn't a Child of A and hovering isn't shared.) - if (menu_is_open && !hovered && g.HoveredWindow == window && !moving_toward_child_menu) + if (menu_is_open && !hovered && g.HoveredWindow == window && !moving_toward_child_menu && !g.NavDisableMouseHover) want_close = true; // Open @@ -7118,23 +7127,32 @@ bool ImGui::BeginMenuEx(const char* label, const char* icon, bool enabled) IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Openable | (menu_is_open ? ImGuiItemStatusFlags_Opened : 0)); PopID(); - if (!menu_is_open && want_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size) + if (want_open && !menu_is_open && g.OpenPopupStack.Size > g.BeginPopupStack.Size) { - // Don't recycle same menu level in the same frame, first close the other menu and yield for a frame. + // Don't reopen/recycle same menu level in the same frame, first close the other menu and yield for a frame. OpenPopup(label); - return false; } - - menu_is_open |= want_open; - if (want_open) + else if (want_open) + { + menu_is_open = true; OpenPopup(label); + } if (menu_is_open) { - SetNextWindowPos(popup_pos, ImGuiCond_Always); // Note: this is super misleading! The value will serve as reference for FindBestWindowPosForPopup(), not actual pos. + ImGuiLastItemData last_item_in_parent = g.LastItemData; + SetNextWindowPos(popup_pos, ImGuiCond_Always); // Note: misleading: the value will serve as reference for FindBestWindowPosForPopup(), not actual pos. PushStyleVar(ImGuiStyleVar_ChildRounding, style.PopupRounding); // First level will use _PopupRounding, subsequent will use _ChildRounding - menu_is_open = BeginPopupEx(id, flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) + menu_is_open = BeginPopupEx(id, window_flags); // menu_is_open can be 'false' when the popup is completely clipped (e.g. zero size display) PopStyleVar(); + if (menu_is_open) + { + // Restore LastItemData so IsItemXXXX functions can work after BeginMenu()/EndMenu() + // (This fixes using IsItemClicked() and IsItemHovered(), but IsItemHovered() also relies on its support for ImGuiItemFlags_NoWindowHoverableCheck) + g.LastItemData = last_item_in_parent; + if (g.HoveredWindow == window) + g.LastItemData.StatusFlags |= ImGuiItemStatusFlags_HoveredWindow; + } } else { @@ -7151,17 +7169,18 @@ bool ImGui::BeginMenu(const char* label, bool enabled) void ImGui::EndMenu() { - // Nav: When a left move request _within our child menu_ failed, close ourselves (the _parent_ menu). - // A menu doesn't close itself because EndMenuBar() wants the catch the last Left<>Right inputs. - // However, it means that with the current code, a BeginMenu() from outside another menu or a menu-bar won't be closable with the Left direction. + // Nav: When a left move request our menu failed, close ourselves. ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; - if (g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet() && window->DC.LayoutType == ImGuiLayoutType_Vertical) - if (g.NavWindow && (g.NavWindow->RootWindowForNav->Flags & ImGuiWindowFlags_Popup) && g.NavWindow->RootWindowForNav->ParentWindow == window) - { - ClosePopupToLevel(g.BeginPopupStack.Size, true); - NavMoveRequestCancel(); - } + IM_ASSERT(window->Flags & ImGuiWindowFlags_Popup); // Mismatched BeginMenu()/EndMenu() calls + ImGuiWindow* parent_window = window->ParentWindow; // Should always be != NULL is we passed assert. + if (window->BeginCount == window->BeginCountPreviousFrame) + if (g.NavMoveDir == ImGuiDir_Left && NavMoveRequestButNoResultYet()) + if (g.NavWindow && (g.NavWindow->RootWindowForNav == window) && parent_window->DC.LayoutType == ImGuiLayoutType_Vertical) + { + ClosePopupToLevel(g.BeginPopupStack.Size - 1, true); + NavMoveRequestCancel(); + } EndPopup(); } @@ -7177,10 +7196,10 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut ImVec2 pos = window->DC.CursorPos; ImVec2 label_size = CalcTextSize(label, NULL, true); + // See BeginMenuEx() for comments about this. const bool menuset_is_open = IsRootOfOpenMenuSet(); - ImGuiWindow* backed_nav_window = g.NavWindow; if (menuset_is_open) - g.NavWindow = window; + PushItemFlag(ImGuiItemFlags_NoWindowHoverableCheck, true); // We've been using the equivalent of ImGuiSelectableFlags_SetNavIdOnHover on all Selectable() since early Nav system days (commit 43ee5d73), // but I am unsure whether this should be kept at all. For now moved it to be an opt-in feature used by menus only. @@ -7189,7 +7208,8 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut if (!enabled) BeginDisabled(); - const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_SetNavIdOnHover; + // We use ImGuiSelectableFlags_NoSetKeyOwner to allow down on one menu item, move, up on another. + const ImGuiSelectableFlags selectable_flags = ImGuiSelectableFlags_SelectOnRelease | ImGuiSelectableFlags_NoSetKeyOwner | ImGuiSelectableFlags_SetNavIdOnHover; const ImGuiMenuColumns* offsets = &window->DC.MenuColumns; if (window->DC.LayoutType == ImGuiLayoutType_Horizontal) { @@ -7201,7 +7221,8 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(style.ItemSpacing.x * 2.0f, style.ItemSpacing.y)); pressed = Selectable("", selected, selectable_flags, ImVec2(w, 0.0f)); PopStyleVar(); - RenderText(text_pos, label); + if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) + RenderText(text_pos, label); window->DC.CursorPos.x += IM_FLOOR(style.ItemSpacing.x * (-1.0f + 0.5f)); // -1 spacing to compensate the spacing added when Selectable() did a SameLine(). It would also work to call SameLine() ourselves after the PopStyleVar(). } else @@ -7215,24 +7236,27 @@ bool ImGui::MenuItemEx(const char* label, const char* icon, const char* shortcut float min_w = window->DC.MenuColumns.DeclColumns(icon_w, label_size.x, shortcut_w, checkmark_w); // Feedback for next frame float stretch_w = ImMax(0.0f, GetContentRegionAvail().x - min_w); pressed = Selectable("", false, selectable_flags | ImGuiSelectableFlags_SpanAvailWidth, ImVec2(min_w, 0.0f)); - RenderText(pos + ImVec2(offsets->OffsetLabel, 0.0f), label); - if (icon_w > 0.0f) - RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); - if (shortcut_w > 0.0f) + if (g.LastItemData.StatusFlags & ImGuiItemStatusFlags_Visible) { - PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); - RenderText(pos + ImVec2(offsets->OffsetShortcut + stretch_w, 0.0f), shortcut, NULL, false); - PopStyleColor(); + RenderText(pos + ImVec2(offsets->OffsetLabel, 0.0f), label); + if (icon_w > 0.0f) + RenderText(pos + ImVec2(offsets->OffsetIcon, 0.0f), icon); + if (shortcut_w > 0.0f) + { + PushStyleColor(ImGuiCol_Text, style.Colors[ImGuiCol_TextDisabled]); + RenderText(pos + ImVec2(offsets->OffsetShortcut + stretch_w, 0.0f), shortcut, NULL, false); + PopStyleColor(); + } + if (selected) + RenderCheckMark(window->DrawList, pos + ImVec2(offsets->OffsetMark + stretch_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(ImGuiCol_Text), g.FontSize * 0.866f); } - if (selected) - RenderCheckMark(window->DrawList, pos + ImVec2(offsets->OffsetMark + stretch_w + g.FontSize * 0.40f, g.FontSize * 0.134f * 0.5f), GetColorU32(ImGuiCol_Text), g.FontSize * 0.866f); } IMGUI_TEST_ENGINE_ITEM_INFO(g.LastItemData.ID, label, g.LastItemData.StatusFlags | ImGuiItemStatusFlags_Checkable | (selected ? ImGuiItemStatusFlags_Checked : 0)); if (!enabled) EndDisabled(); PopID(); if (menuset_is_open) - g.NavWindow = backed_nav_window; + PopItemFlag(); return pressed; } @@ -7284,7 +7308,7 @@ struct ImGuiTabBarSection namespace ImGui { static void TabBarLayout(ImGuiTabBar* tab_bar); - static ImU32 TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label); + static ImU32 TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label, ImGuiWindow* docked_window); static float TabBarCalcMaxTabWidth(); static float TabBarScrollClamp(ImGuiTabBar* tab_bar, float scrolling); static void TabBarScrollToTab(ImGuiTabBar* tab_bar, ImGuiID tab_id, ImGuiTabBarSection* sections); @@ -7550,8 +7574,8 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) // Additionally, when using TabBarAddTab() to manipulate tab bar order we occasionally insert new tabs that don't have a width yet, // and we cannot wait for the next BeginTabItem() call. We cannot compute this width within TabBarAddTab() because font size depends on the active window. const char* tab_name = tab_bar->GetTabName(tab); - const bool has_close_button = (tab->Flags & ImGuiTabItemFlags_NoCloseButton) ? false : true; - tab->ContentWidth = (tab->RequestedWidth > 0.0f) ? tab->RequestedWidth : TabItemCalcSize(tab_name, has_close_button).x; + const bool has_close_button_or_unsaved_marker = (tab->Flags & ImGuiTabItemFlags_NoCloseButton) == 0 || (tab->Flags & ImGuiTabItemFlags_UnsavedDocument); + tab->ContentWidth = (tab->RequestedWidth >= 0.0f) ? tab->RequestedWidth : TabItemCalcSize(tab_name, has_close_button_or_unsaved_marker).x; int section_n = TabItemGetSectionIdx(tab); ImGuiTabBarSection* section = §ions[section_n]; @@ -7563,9 +7587,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) ImGuiShrinkWidthItem* shrink_width_item = &g.ShrinkWidthBuffer[shrink_buffer_indexes[section_n]++]; shrink_width_item->Index = tab_n; shrink_width_item->Width = shrink_width_item->InitialWidth = tab->ContentWidth; - - IM_ASSERT(tab->ContentWidth > 0.0f); - tab->Width = tab->ContentWidth; + tab->Width = ImMax(tab->ContentWidth, 1.0f); } // Compute total ideal width (used for e.g. auto-resizing a window) @@ -7595,7 +7617,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) width_excess = (section_0_w + section_2_w) - tab_bar->BarRect.GetWidth(); // Excess used to shrink leading/trailing section // With ImGuiTabBarFlags_FittingPolicyScroll policy, we will only shrink leading/trailing if the central section is not visible anymore - if (width_excess > 0.0f && ((tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyResizeDown) || !central_section_is_visible)) + if (width_excess >= 1.0f && ((tab_bar->Flags & ImGuiTabBarFlags_FittingPolicyResizeDown) || !central_section_is_visible)) { int shrink_data_count = (central_section_is_visible ? sections[1].TabCount : sections[0].TabCount + sections[2].TabCount); int shrink_data_offset = (central_section_is_visible ? sections[0].TabCount + sections[2].TabCount : 0); @@ -7609,6 +7631,7 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) if (shrinked_width < 0.0f) continue; + shrinked_width = ImMax(1.0f, shrinked_width); int section_n = TabItemGetSectionIdx(tab); sections[section_n].Width -= (tab->Width - shrinked_width); tab->Width = shrinked_width; @@ -7678,9 +7701,11 @@ static void ImGui::TabBarLayout(ImGuiTabBar* tab_bar) window->DC.IdealMaxPos.x = ImMax(window->DC.IdealMaxPos.x, tab_bar->BarRect.Min.x + tab_bar->WidthAllTabsIdeal); } -// Dockables uses Name/ID in the global namespace. Non-dockable items use the ID stack. -static ImU32 ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label) +// Dockable windows uses Name/ID in the global namespace. Non-dockable items use the ID stack. +static ImU32 ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label, ImGuiWindow* docked_window) { + IM_ASSERT(docked_window == NULL); // master branch only + IM_UNUSED(docked_window); if (tab_bar->Flags & ImGuiTabBarFlags_DockNode) { ImGuiID id = ImHashStr(label); @@ -7722,7 +7747,9 @@ void ImGui::TabBarRemoveTab(ImGuiTabBar* tab_bar, ImGuiID tab_id) // Called on manual closure attempt void ImGui::TabBarCloseTab(ImGuiTabBar* tab_bar, ImGuiTabItem* tab) { - IM_ASSERT(!(tab->Flags & ImGuiTabItemFlags_Button)); + if (tab->Flags & ImGuiTabItemFlags_Button) + return; // A button appended with TabItemButton(). + if (!(tab->Flags & ImGuiTabItemFlags_UnsavedDocument)) { // This will remove a frame of lag for selecting another tab on closure. @@ -7985,7 +8012,7 @@ bool ImGui::BeginTabItem(const char* label, bool* p_open, ImGuiTabItemFlags f } IM_ASSERT(!(flags & ImGuiTabItemFlags_Button)); // BeginTabItem() Can't be used with button flags, use TabItemButton() instead! - bool ret = TabItemEx(tab_bar, label, p_open, flags); + bool ret = TabItemEx(tab_bar, label, p_open, flags, NULL); if (ret && !(flags & ImGuiTabItemFlags_NoPushId)) { ImGuiTabItem* tab = &tab_bar->Tabs[tab_bar->LastTabItemIdx]; @@ -8026,10 +8053,10 @@ bool ImGui::TabItemButton(const char* label, ImGuiTabItemFlags flags) IM_ASSERT_USER_ERROR(tab_bar != NULL, "Needs to be called between BeginTabBar() and EndTabBar()!"); return false; } - return TabItemEx(tab_bar, label, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder); + return TabItemEx(tab_bar, label, NULL, flags | ImGuiTabItemFlags_Button | ImGuiTabItemFlags_NoReorder, NULL); } -bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags) +bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, ImGuiTabItemFlags flags, ImGuiWindow* docked_window) { // Layout whole tab bar if not already done ImGuiContext& g = *GImGui; @@ -8044,14 +8071,14 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, return false; const ImGuiStyle& style = g.Style; - const ImGuiID id = TabBarCalcTabID(tab_bar, label); + const ImGuiID id = TabBarCalcTabID(tab_bar, label, docked_window); // If the user called us with *p_open == false, we early out and don't render. // We make a call to ItemAdd() so that attempts to use a contextual popup menu with an implicit ID won't use an older ID. IMGUI_TEST_ENGINE_ITEM_INFO(id, label, g.LastItemData.StatusFlags); if (p_open && !*p_open) { - ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus); + ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav); return false; } @@ -8077,25 +8104,33 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, tab_bar->LastTabItemIdx = (ImS16)tab_bar->Tabs.index_from_ptr(tab); // Calculate tab contents size - ImVec2 size = TabItemCalcSize(label, p_open != NULL); + ImVec2 size = TabItemCalcSize(label, (p_open != NULL) || (flags & ImGuiTabItemFlags_UnsavedDocument)); tab->RequestedWidth = -1.0f; if (g.NextItemData.Flags & ImGuiNextItemDataFlags_HasWidth) size.x = tab->RequestedWidth = g.NextItemData.Width; if (tab_is_new) - tab->Width = size.x; + tab->Width = ImMax(1.0f, size.x); tab->ContentWidth = size.x; tab->BeginOrder = tab_bar->TabsActiveCount++; const bool tab_bar_appearing = (tab_bar->PrevFrameVisible + 1 < g.FrameCount); const bool tab_bar_focused = (tab_bar->Flags & ImGuiTabBarFlags_IsFocused) != 0; const bool tab_appearing = (tab->LastFrameVisible + 1 < g.FrameCount); + const bool tab_just_unsaved = (flags & ImGuiTabItemFlags_UnsavedDocument) && !(tab->Flags & ImGuiTabItemFlags_UnsavedDocument); const bool is_tab_button = (flags & ImGuiTabItemFlags_Button) != 0; tab->LastFrameVisible = g.FrameCount; tab->Flags = flags; - // Append name with zero-terminator - tab->NameOffset = (ImS32)tab_bar->TabsNames.size(); - tab_bar->TabsNames.append(label, label + strlen(label) + 1); + // Append name _WITH_ the zero-terminator + if (docked_window != NULL) + { + IM_ASSERT(docked_window == NULL); // master branch only + } + else + { + tab->NameOffset = (ImS32)tab_bar->TabsNames.size(); + tab_bar->TabsNames.append(label, label + strlen(label) + 1); + } // Update selected tab if (!is_tab_button) @@ -8122,7 +8157,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // and then gets submitted again, the tabs will have 'tab_appearing=true' but 'tab_is_new=false'. if (tab_appearing && (!tab_bar_appearing || tab_is_new)) { - ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav | ImGuiItemFlags_NoNavDefaultFocus); + ItemAdd(ImRect(), id, NULL, ImGuiItemFlags_NoNav); if (is_tab_button) return false; return tab_contents_visible; @@ -8220,7 +8255,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, const ImGuiID close_button_id = p_open ? GetIDWithSeed("#CLOSE", NULL, id) : 0; bool just_closed; bool text_clipped; - TabItemLabelAndCloseButton(display_draw_list, bb, flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible, &just_closed, &text_clipped); + TabItemLabelAndCloseButton(display_draw_list, bb, tab_just_unsaved ? (flags & ~ImGuiTabItemFlags_UnsavedDocument) : flags, tab_bar->FramePadding, label, id, close_button_id, tab_contents_visible, &just_closed, &text_clipped); if (just_closed && p_open != NULL) { *p_open = false; @@ -8237,9 +8272,10 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // (We test IsItemHovered() to discard e.g. when another item is active or drag and drop over the tab bar, which g.HoveredId ignores) // FIXME: This is a mess. // FIXME: We may want disabled tab to still display the tooltip? - if (text_clipped && g.HoveredId == id && !held && g.HoveredIdNotActiveTimer > g.TooltipSlowDelay && IsItemHovered()) + if (text_clipped && g.HoveredId == id && !held) if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip)) - SetTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); + if (IsItemHovered(ImGuiHoveredFlags_DelayNormal)) + SetTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); IM_ASSERT(!is_tab_button || !(tab_bar->SelectedTabId == tab->ID && is_tab_button)); // TabItemButton should not be selected if (is_tab_button) @@ -8257,24 +8293,30 @@ void ImGui::SetTabItemClosed(const char* label) if (is_within_manual_tab_bar) { ImGuiTabBar* tab_bar = g.CurrentTabBar; - ImGuiID tab_id = TabBarCalcTabID(tab_bar, label); + ImGuiID tab_id = TabBarCalcTabID(tab_bar, label, NULL); if (ImGuiTabItem* tab = TabBarFindTabByID(tab_bar, tab_id)) tab->WantClose = true; // Will be processed by next call to TabBarLayout() } } -ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button) +ImVec2 ImGui::TabItemCalcSize(const char* label, bool has_close_button_or_unsaved_marker) { ImGuiContext& g = *GImGui; ImVec2 label_size = CalcTextSize(label, NULL, true); ImVec2 size = ImVec2(label_size.x + g.Style.FramePadding.x, label_size.y + g.Style.FramePadding.y * 2.0f); - if (has_close_button) + if (has_close_button_or_unsaved_marker) size.x += g.Style.FramePadding.x + (g.Style.ItemInnerSpacing.x + g.FontSize); // We use Y intentionally to fit the close button circle. else size.x += g.Style.FramePadding.x + 1.0f; return ImVec2(ImMin(size.x, TabBarCalcMaxTabWidth()), size.y); } +ImVec2 ImGui::TabItemCalcSize(ImGuiWindow*) +{ + IM_ASSERT(0); // This function exists to facilitate merge with 'docking' branch. + return ImVec2(0.0f, 0.0f); +} + void ImGui::TabItemBackground(ImDrawList* draw_list, const ImRect& bb, ImGuiTabItemFlags flags, ImU32 col) { // While rendering tabs, we trim 1 pixel off the top of our bounding box so they can fit within a regular frame height while looking "detached" from it. diff --git a/extern/src/imgui/imstb_textedit.h b/extern/src/imgui/imstb_textedit.h index 75a159da..a8a82311 100644 --- a/extern/src/imgui/imstb_textedit.h +++ b/extern/src/imgui/imstb_textedit.h @@ -2,6 +2,7 @@ // This is a slightly modified version of stb_textedit.h 1.14. // Those changes would need to be pushed into nothings/stb: // - Fix in stb_textedit_discard_redo (see https://github.com/nothings/stb/issues/321) +// - Fix in stb_textedit_find_charpos to handle last line (see https://github.com/ocornut/imgui/issues/6000) // Grep for [DEAR IMGUI] to find the changes. // stb_textedit.h - v1.14 - public domain - Sean Barrett @@ -524,29 +525,14 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s int z = STB_TEXTEDIT_STRINGLEN(str); int i=0, first; - if (n == z) { - // if it's at the end, then find the last line -- simpler than trying to - // explicitly handle this case in the regular code - if (single_line) { - STB_TEXTEDIT_LAYOUTROW(&r, str, 0); - find->y = 0; - find->first_char = 0; - find->length = z; - find->height = r.ymax - r.ymin; - find->x = r.x1; - } else { - find->y = 0; - find->x = 0; - find->height = 1; - while (i < z) { - STB_TEXTEDIT_LAYOUTROW(&r, str, i); - prev_start = i; - i += r.num_chars; - } - find->first_char = i; - find->length = 0; - find->prev_first = prev_start; - } + if (n == z && single_line) { + // special case if it's at the end (may not be needed?) + STB_TEXTEDIT_LAYOUTROW(&r, str, 0); + find->y = 0; + find->first_char = 0; + find->length = z; + find->height = r.ymax - r.ymin; + find->x = r.x1; return; } @@ -557,9 +543,13 @@ static void stb_textedit_find_charpos(StbFindState *find, STB_TEXTEDIT_STRING *s STB_TEXTEDIT_LAYOUTROW(&r, str, i); if (n < i + r.num_chars) break; + if (i + r.num_chars == z && z > 0 && STB_TEXTEDIT_GETCHAR(str, z - 1) != STB_TEXTEDIT_NEWLINE) // [DEAR IMGUI] special handling for last line + break; // [DEAR IMGUI] prev_start = i; i += r.num_chars; find->y += r.baseline_y_delta; + if (i == z) // [DEAR IMGUI] + break; // [DEAR IMGUI] } find->first_char = first = i; diff --git a/extern/src/imgui/imstb_truetype.h b/extern/src/imgui/imstb_truetype.h index 643d3789..35c827e6 100644 --- a/extern/src/imgui/imstb_truetype.h +++ b/extern/src/imgui/imstb_truetype.h @@ -2008,7 +2008,7 @@ static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int gly start = end; } } - if (fdselector == -1) stbtt__new_buf(NULL, 0); + if (fdselector == -1) return stbtt__new_buf(NULL, 0); // [DEAR IMGUI] fixed, see #6007 and nothings/stb#1422 return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector)); } diff --git a/extern/src/vk_mem_alloc.h b/extern/src/vk_mem_alloc.h new file mode 100644 index 00000000..b787c36d --- /dev/null +++ b/extern/src/vk_mem_alloc.h @@ -0,0 +1,19612 @@ +// +// Copyright (c) 2017-2022 Advanced Micro Devices, Inc. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// + +#ifndef AMD_VULKAN_MEMORY_ALLOCATOR_H +#define AMD_VULKAN_MEMORY_ALLOCATOR_H + +/** \mainpage Vulkan Memory Allocator + +Version 3.1.0-development + +Copyright (c) 2017-2022 Advanced Micro Devices, Inc. All rights reserved. \n +License: MIT + +API documentation divided into groups: [Modules](modules.html) + +\section main_table_of_contents Table of contents + +- User guide + - \subpage quick_start + - [Project setup](@ref quick_start_project_setup) + - [Initialization](@ref quick_start_initialization) + - [Resource allocation](@ref quick_start_resource_allocation) + - \subpage choosing_memory_type + - [Usage](@ref choosing_memory_type_usage) + - [Required and preferred flags](@ref choosing_memory_type_required_preferred_flags) + - [Explicit memory types](@ref choosing_memory_type_explicit_memory_types) + - [Custom memory pools](@ref choosing_memory_type_custom_memory_pools) + - [Dedicated allocations](@ref choosing_memory_type_dedicated_allocations) + - \subpage memory_mapping + - [Mapping functions](@ref memory_mapping_mapping_functions) + - [Persistently mapped memory](@ref memory_mapping_persistently_mapped_memory) + - [Cache flush and invalidate](@ref memory_mapping_cache_control) + - \subpage staying_within_budget + - [Querying for budget](@ref staying_within_budget_querying_for_budget) + - [Controlling memory usage](@ref staying_within_budget_controlling_memory_usage) + - \subpage resource_aliasing + - \subpage custom_memory_pools + - [Choosing memory type index](@ref custom_memory_pools_MemTypeIndex) + - [Linear allocation algorithm](@ref linear_algorithm) + - [Free-at-once](@ref linear_algorithm_free_at_once) + - [Stack](@ref linear_algorithm_stack) + - [Double stack](@ref linear_algorithm_double_stack) + - [Ring buffer](@ref linear_algorithm_ring_buffer) + - \subpage defragmentation + - \subpage statistics + - [Numeric statistics](@ref statistics_numeric_statistics) + - [JSON dump](@ref statistics_json_dump) + - \subpage allocation_annotation + - [Allocation user data](@ref allocation_user_data) + - [Allocation names](@ref allocation_names) + - \subpage virtual_allocator + - \subpage debugging_memory_usage + - [Memory initialization](@ref debugging_memory_usage_initialization) + - [Margins](@ref debugging_memory_usage_margins) + - [Corruption detection](@ref debugging_memory_usage_corruption_detection) + - \subpage opengl_interop +- \subpage usage_patterns + - [GPU-only resource](@ref usage_patterns_gpu_only) + - [Staging copy for upload](@ref usage_patterns_staging_copy_upload) + - [Readback](@ref usage_patterns_readback) + - [Advanced data uploading](@ref usage_patterns_advanced_data_uploading) + - [Other use cases](@ref usage_patterns_other_use_cases) +- \subpage configuration + - [Pointers to Vulkan functions](@ref config_Vulkan_functions) + - [Custom host memory allocator](@ref custom_memory_allocator) + - [Device memory allocation callbacks](@ref allocation_callbacks) + - [Device heap memory limit](@ref heap_memory_limit) +- Extension support + - \subpage vk_khr_dedicated_allocation + - \subpage enabling_buffer_device_address + - \subpage vk_ext_memory_priority + - \subpage vk_amd_device_coherent_memory +- \subpage general_considerations + - [Thread safety](@ref general_considerations_thread_safety) + - [Versioning and compatibility](@ref general_considerations_versioning_and_compatibility) + - [Validation layer warnings](@ref general_considerations_validation_layer_warnings) + - [Allocation algorithm](@ref general_considerations_allocation_algorithm) + - [Features not supported](@ref general_considerations_features_not_supported) + +\section main_see_also See also + +- [**Product page on GPUOpen**](https://gpuopen.com/gaming-product/vulkan-memory-allocator/) +- [**Source repository on GitHub**](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator) + +\defgroup group_init Library initialization + +\brief API elements related to the initialization and management of the entire library, especially #VmaAllocator object. + +\defgroup group_alloc Memory allocation + +\brief API elements related to the allocation, deallocation, and management of Vulkan memory, buffers, images. +Most basic ones being: vmaCreateBuffer(), vmaCreateImage(). + +\defgroup group_virtual Virtual allocator + +\brief API elements related to the mechanism of \ref virtual_allocator - using the core allocation algorithm +for user-defined purpose without allocating any real GPU memory. + +\defgroup group_stats Statistics + +\brief API elements that query current status of the allocator, from memory usage, budget, to full dump of the internal state in JSON format. +See documentation chapter: \ref statistics. +*/ + + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef VULKAN_H_ + #include +#endif + +#if !defined(VMA_VULKAN_VERSION) + #if defined(VK_VERSION_1_3) + #define VMA_VULKAN_VERSION 1003000 + #elif defined(VK_VERSION_1_2) + #define VMA_VULKAN_VERSION 1002000 + #elif defined(VK_VERSION_1_1) + #define VMA_VULKAN_VERSION 1001000 + #else + #define VMA_VULKAN_VERSION 1000000 + #endif +#endif + +#if defined(__ANDROID__) && defined(VK_NO_PROTOTYPES) && VMA_STATIC_VULKAN_FUNCTIONS + extern PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; + extern PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr; + extern PFN_vkGetPhysicalDeviceProperties vkGetPhysicalDeviceProperties; + extern PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties; + extern PFN_vkAllocateMemory vkAllocateMemory; + extern PFN_vkFreeMemory vkFreeMemory; + extern PFN_vkMapMemory vkMapMemory; + extern PFN_vkUnmapMemory vkUnmapMemory; + extern PFN_vkFlushMappedMemoryRanges vkFlushMappedMemoryRanges; + extern PFN_vkInvalidateMappedMemoryRanges vkInvalidateMappedMemoryRanges; + extern PFN_vkBindBufferMemory vkBindBufferMemory; + extern PFN_vkBindImageMemory vkBindImageMemory; + extern PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements; + extern PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements; + extern PFN_vkCreateBuffer vkCreateBuffer; + extern PFN_vkDestroyBuffer vkDestroyBuffer; + extern PFN_vkCreateImage vkCreateImage; + extern PFN_vkDestroyImage vkDestroyImage; + extern PFN_vkCmdCopyBuffer vkCmdCopyBuffer; + #if VMA_VULKAN_VERSION >= 1001000 + extern PFN_vkGetBufferMemoryRequirements2 vkGetBufferMemoryRequirements2; + extern PFN_vkGetImageMemoryRequirements2 vkGetImageMemoryRequirements2; + extern PFN_vkBindBufferMemory2 vkBindBufferMemory2; + extern PFN_vkBindImageMemory2 vkBindImageMemory2; + extern PFN_vkGetPhysicalDeviceMemoryProperties2 vkGetPhysicalDeviceMemoryProperties2; + #endif // #if VMA_VULKAN_VERSION >= 1001000 +#endif // #if defined(__ANDROID__) && VMA_STATIC_VULKAN_FUNCTIONS && VK_NO_PROTOTYPES + +#if !defined(VMA_DEDICATED_ALLOCATION) + #if VK_KHR_get_memory_requirements2 && VK_KHR_dedicated_allocation + #define VMA_DEDICATED_ALLOCATION 1 + #else + #define VMA_DEDICATED_ALLOCATION 0 + #endif +#endif + +#if !defined(VMA_BIND_MEMORY2) + #if VK_KHR_bind_memory2 + #define VMA_BIND_MEMORY2 1 + #else + #define VMA_BIND_MEMORY2 0 + #endif +#endif + +#if !defined(VMA_MEMORY_BUDGET) + #if VK_EXT_memory_budget && (VK_KHR_get_physical_device_properties2 || VMA_VULKAN_VERSION >= 1001000) + #define VMA_MEMORY_BUDGET 1 + #else + #define VMA_MEMORY_BUDGET 0 + #endif +#endif + +// Defined to 1 when VK_KHR_buffer_device_address device extension or equivalent core Vulkan 1.2 feature is defined in its headers. +#if !defined(VMA_BUFFER_DEVICE_ADDRESS) + #if VK_KHR_buffer_device_address || VMA_VULKAN_VERSION >= 1002000 + #define VMA_BUFFER_DEVICE_ADDRESS 1 + #else + #define VMA_BUFFER_DEVICE_ADDRESS 0 + #endif +#endif + +// Defined to 1 when VK_EXT_memory_priority device extension is defined in Vulkan headers. +#if !defined(VMA_MEMORY_PRIORITY) + #if VK_EXT_memory_priority + #define VMA_MEMORY_PRIORITY 1 + #else + #define VMA_MEMORY_PRIORITY 0 + #endif +#endif + +// Defined to 1 when VK_KHR_external_memory device extension is defined in Vulkan headers. +#if !defined(VMA_EXTERNAL_MEMORY) + #if VK_KHR_external_memory + #define VMA_EXTERNAL_MEMORY 1 + #else + #define VMA_EXTERNAL_MEMORY 0 + #endif +#endif + +// Define these macros to decorate all public functions with additional code, +// before and after returned type, appropriately. This may be useful for +// exporting the functions when compiling VMA as a separate library. Example: +// #define VMA_CALL_PRE __declspec(dllexport) +// #define VMA_CALL_POST __cdecl +#ifndef VMA_CALL_PRE + #define VMA_CALL_PRE +#endif +#ifndef VMA_CALL_POST + #define VMA_CALL_POST +#endif + +// Define this macro to decorate pointers with an attribute specifying the +// length of the array they point to if they are not null. +// +// The length may be one of +// - The name of another parameter in the argument list where the pointer is declared +// - The name of another member in the struct where the pointer is declared +// - The name of a member of a struct type, meaning the value of that member in +// the context of the call. For example +// VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount"), +// this means the number of memory heaps available in the device associated +// with the VmaAllocator being dealt with. +#ifndef VMA_LEN_IF_NOT_NULL + #define VMA_LEN_IF_NOT_NULL(len) +#endif + +// The VMA_NULLABLE macro is defined to be _Nullable when compiling with Clang. +// see: https://clang.llvm.org/docs/AttributeReference.html#nullable +#ifndef VMA_NULLABLE + #ifdef __clang__ + #define VMA_NULLABLE _Nullable + #else + #define VMA_NULLABLE + #endif +#endif + +// The VMA_NOT_NULL macro is defined to be _Nonnull when compiling with Clang. +// see: https://clang.llvm.org/docs/AttributeReference.html#nonnull +#ifndef VMA_NOT_NULL + #ifdef __clang__ + #define VMA_NOT_NULL _Nonnull + #else + #define VMA_NOT_NULL + #endif +#endif + +// If non-dispatchable handles are represented as pointers then we can give +// then nullability annotations +#ifndef VMA_NOT_NULL_NON_DISPATCHABLE + #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) + #define VMA_NOT_NULL_NON_DISPATCHABLE VMA_NOT_NULL + #else + #define VMA_NOT_NULL_NON_DISPATCHABLE + #endif +#endif + +#ifndef VMA_NULLABLE_NON_DISPATCHABLE + #if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__) + #define VMA_NULLABLE_NON_DISPATCHABLE VMA_NULLABLE + #else + #define VMA_NULLABLE_NON_DISPATCHABLE + #endif +#endif + +#ifndef VMA_STATS_STRING_ENABLED + #define VMA_STATS_STRING_ENABLED 1 +#endif + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// INTERFACE +// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +// Sections for managing code placement in file, only for development purposes e.g. for convenient folding inside an IDE. +#ifndef _VMA_ENUM_DECLARATIONS + +/** +\addtogroup group_init +@{ +*/ + +/// Flags for created #VmaAllocator. +typedef enum VmaAllocatorCreateFlagBits +{ + /** \brief Allocator and all objects created from it will not be synchronized internally, so you must guarantee they are used from only one thread at a time or synchronized externally by you. + + Using this flag may increase performance because internal mutexes are not used. + */ + VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT = 0x00000001, + /** \brief Enables usage of VK_KHR_dedicated_allocation extension. + + The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`. + When it is `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1. + + Using this extension will automatically allocate dedicated blocks of memory for + some buffers and images instead of suballocating place for them out of bigger + memory blocks (as if you explicitly used #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT + flag) when it is recommended by the driver. It may improve performance on some + GPUs. + + You may set this flag only if you found out that following device extensions are + supported, you enabled them while creating Vulkan device passed as + VmaAllocatorCreateInfo::device, and you want them to be used internally by this + library: + + - VK_KHR_get_memory_requirements2 (device extension) + - VK_KHR_dedicated_allocation (device extension) + + When this flag is set, you can experience following warnings reported by Vulkan + validation layer. You can ignore them. + + > vkBindBufferMemory(): Binding memory to buffer 0x2d but vkGetBufferMemoryRequirements() has not been called on that buffer. + */ + VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT = 0x00000002, + /** + Enables usage of VK_KHR_bind_memory2 extension. + + The flag works only if VmaAllocatorCreateInfo::vulkanApiVersion `== VK_API_VERSION_1_0`. + When it is `VK_API_VERSION_1_1`, the flag is ignored because the extension has been promoted to Vulkan 1.1. + + You may set this flag only if you found out that this device extension is supported, + you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device, + and you want it to be used internally by this library. + + The extension provides functions `vkBindBufferMemory2KHR` and `vkBindImageMemory2KHR`, + which allow to pass a chain of `pNext` structures while binding. + This flag is required if you use `pNext` parameter in vmaBindBufferMemory2() or vmaBindImageMemory2(). + */ + VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT = 0x00000004, + /** + Enables usage of VK_EXT_memory_budget extension. + + You may set this flag only if you found out that this device extension is supported, + you enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device, + and you want it to be used internally by this library, along with another instance extension + VK_KHR_get_physical_device_properties2, which is required by it (or Vulkan 1.1, where this extension is promoted). + + The extension provides query for current memory usage and budget, which will probably + be more accurate than an estimation used by the library otherwise. + */ + VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT = 0x00000008, + /** + Enables usage of VK_AMD_device_coherent_memory extension. + + You may set this flag only if you: + + - found out that this device extension is supported and enabled it while creating Vulkan device passed as VmaAllocatorCreateInfo::device, + - checked that `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true and set it while creating the Vulkan device, + - want it to be used internally by this library. + + The extension and accompanying device feature provide access to memory types with + `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flags. + They are useful mostly for writing breadcrumb markers - a common method for debugging GPU crash/hang/TDR. + + When the extension is not enabled, such memory types are still enumerated, but their usage is illegal. + To protect from this error, if you don't create the allocator with this flag, it will refuse to allocate any memory or create a custom pool in such memory type, + returning `VK_ERROR_FEATURE_NOT_PRESENT`. + */ + VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT = 0x00000010, + /** + Enables usage of "buffer device address" feature, which allows you to use function + `vkGetBufferDeviceAddress*` to get raw GPU pointer to a buffer and pass it for usage inside a shader. + + You may set this flag only if you: + + 1. (For Vulkan version < 1.2) Found as available and enabled device extension + VK_KHR_buffer_device_address. + This extension is promoted to core Vulkan 1.2. + 2. Found as available and enabled device feature `VkPhysicalDeviceBufferDeviceAddressFeatures::bufferDeviceAddress`. + + When this flag is set, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT` using VMA. + The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT` to + allocated memory blocks wherever it might be needed. + + For more information, see documentation chapter \ref enabling_buffer_device_address. + */ + VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT = 0x00000020, + /** + Enables usage of VK_EXT_memory_priority extension in the library. + + You may set this flag only if you found available and enabled this device extension, + along with `VkPhysicalDeviceMemoryPriorityFeaturesEXT::memoryPriority == VK_TRUE`, + while creating Vulkan device passed as VmaAllocatorCreateInfo::device. + + When this flag is used, VmaAllocationCreateInfo::priority and VmaPoolCreateInfo::priority + are used to set priorities of allocated Vulkan memory. Without it, these variables are ignored. + + A priority must be a floating-point value between 0 and 1, indicating the priority of the allocation relative to other memory allocations. + Larger values are higher priority. The granularity of the priorities is implementation-dependent. + It is automatically passed to every call to `vkAllocateMemory` done by the library using structure `VkMemoryPriorityAllocateInfoEXT`. + The value to be used for default priority is 0.5. + For more details, see the documentation of the VK_EXT_memory_priority extension. + */ + VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT = 0x00000040, + + VMA_ALLOCATOR_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaAllocatorCreateFlagBits; +/// See #VmaAllocatorCreateFlagBits. +typedef VkFlags VmaAllocatorCreateFlags; + +/** @} */ + +/** +\addtogroup group_alloc +@{ +*/ + +/// \brief Intended usage of the allocated memory. +typedef enum VmaMemoryUsage +{ + /** No intended memory usage specified. + Use other members of VmaAllocationCreateInfo to specify your requirements. + */ + VMA_MEMORY_USAGE_UNKNOWN = 0, + /** + \deprecated Obsolete, preserved for backward compatibility. + Prefers `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`. + */ + VMA_MEMORY_USAGE_GPU_ONLY = 1, + /** + \deprecated Obsolete, preserved for backward compatibility. + Guarantees `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` and `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT`. + */ + VMA_MEMORY_USAGE_CPU_ONLY = 2, + /** + \deprecated Obsolete, preserved for backward compatibility. + Guarantees `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`, prefers `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`. + */ + VMA_MEMORY_USAGE_CPU_TO_GPU = 3, + /** + \deprecated Obsolete, preserved for backward compatibility. + Guarantees `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`, prefers `VK_MEMORY_PROPERTY_HOST_CACHED_BIT`. + */ + VMA_MEMORY_USAGE_GPU_TO_CPU = 4, + /** + \deprecated Obsolete, preserved for backward compatibility. + Prefers not `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`. + */ + VMA_MEMORY_USAGE_CPU_COPY = 5, + /** + Lazily allocated GPU memory having `VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT`. + Exists mostly on mobile platforms. Using it on desktop PC or other GPUs with no such memory type present will fail the allocation. + + Usage: Memory for transient attachment images (color attachments, depth attachments etc.), created with `VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT`. + + Allocations with this usage are always created as dedicated - it implies #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. + */ + VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED = 6, + /** + Selects best memory type automatically. + This flag is recommended for most common use cases. + + When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT), + you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT + in VmaAllocationCreateInfo::flags. + + It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g. + vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo() + and not with generic memory allocation functions. + */ + VMA_MEMORY_USAGE_AUTO = 7, + /** + Selects best memory type automatically with preference for GPU (device) memory. + + When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT), + you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT + in VmaAllocationCreateInfo::flags. + + It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g. + vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo() + and not with generic memory allocation functions. + */ + VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE = 8, + /** + Selects best memory type automatically with preference for CPU (host) memory. + + When using this flag, if you want to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT), + you must pass one of the flags: #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT + in VmaAllocationCreateInfo::flags. + + It can be used only with functions that let the library know `VkBufferCreateInfo` or `VkImageCreateInfo`, e.g. + vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo() + and not with generic memory allocation functions. + */ + VMA_MEMORY_USAGE_AUTO_PREFER_HOST = 9, + + VMA_MEMORY_USAGE_MAX_ENUM = 0x7FFFFFFF +} VmaMemoryUsage; + +/// Flags to be passed as VmaAllocationCreateInfo::flags. +typedef enum VmaAllocationCreateFlagBits +{ + /** \brief Set this flag if the allocation should have its own memory block. + + Use it for special, big resources, like fullscreen images used as attachments. + */ + VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT = 0x00000001, + + /** \brief Set this flag to only try to allocate from existing `VkDeviceMemory` blocks and never create new such block. + + If new allocation cannot be placed in any of the existing blocks, allocation + fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY` error. + + You should not use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT and + #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT at the same time. It makes no sense. + */ + VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT = 0x00000002, + /** \brief Set this flag to use a memory that will be persistently mapped and retrieve pointer to it. + + Pointer to mapped memory will be returned through VmaAllocationInfo::pMappedData. + + It is valid to use this flag for allocation made from memory type that is not + `HOST_VISIBLE`. This flag is then ignored and memory is not mapped. This is + useful if you need an allocation that is efficient to use on GPU + (`DEVICE_LOCAL`) and still want to map it directly if possible on platforms that + support it (e.g. Intel GPU). + */ + VMA_ALLOCATION_CREATE_MAPPED_BIT = 0x00000004, + /** \deprecated Preserved for backward compatibility. Consider using vmaSetAllocationName() instead. + + Set this flag to treat VmaAllocationCreateInfo::pUserData as pointer to a + null-terminated string. Instead of copying pointer value, a local copy of the + string is made and stored in allocation's `pName`. The string is automatically + freed together with the allocation. It is also used in vmaBuildStatsString(). + */ + VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT = 0x00000020, + /** Allocation will be created from upper stack in a double stack pool. + + This flag is only allowed for custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT flag. + */ + VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = 0x00000040, + /** Create both buffer/image and allocation, but don't bind them together. + It is useful when you want to bind yourself to do some more advanced binding, e.g. using some extensions. + The flag is meaningful only with functions that bind by default: vmaCreateBuffer(), vmaCreateImage(). + Otherwise it is ignored. + + If you want to make sure the new buffer/image is not tied to the new memory allocation + through `VkMemoryDedicatedAllocateInfoKHR` structure in case the allocation ends up in its own memory block, + use also flag #VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT. + */ + VMA_ALLOCATION_CREATE_DONT_BIND_BIT = 0x00000080, + /** Create allocation only if additional device memory required for it, if any, won't exceed + memory budget. Otherwise return `VK_ERROR_OUT_OF_DEVICE_MEMORY`. + */ + VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT = 0x00000100, + /** \brief Set this flag if the allocated memory will have aliasing resources. + + Usage of this flag prevents supplying `VkMemoryDedicatedAllocateInfoKHR` when #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT is specified. + Otherwise created dedicated memory will not be suitable for aliasing resources, resulting in Vulkan Validation Layer errors. + */ + VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT = 0x00000200, + /** + Requests possibility to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT). + + - If you use #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` value, + you must use this flag to be able to map the allocation. Otherwise, mapping is incorrect. + - If you use other value of #VmaMemoryUsage, this flag is ignored and mapping is always possible in memory types that are `HOST_VISIBLE`. + This includes allocations created in \ref custom_memory_pools. + + Declares that mapped memory will only be written sequentially, e.g. using `memcpy()` or a loop writing number-by-number, + never read or accessed randomly, so a memory type can be selected that is uncached and write-combined. + + \warning Violating this declaration may work correctly, but will likely be very slow. + Watch out for implicit reads introduced by doing e.g. `pMappedData[i] += x;` + Better prepare your data in a local variable and `memcpy()` it to the mapped pointer all at once. + */ + VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT = 0x00000400, + /** + Requests possibility to map the allocation (using vmaMapMemory() or #VMA_ALLOCATION_CREATE_MAPPED_BIT). + + - If you use #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` value, + you must use this flag to be able to map the allocation. Otherwise, mapping is incorrect. + - If you use other value of #VmaMemoryUsage, this flag is ignored and mapping is always possible in memory types that are `HOST_VISIBLE`. + This includes allocations created in \ref custom_memory_pools. + + Declares that mapped memory can be read, written, and accessed in random order, + so a `HOST_CACHED` memory type is required. + */ + VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT = 0x00000800, + /** + Together with #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT, + it says that despite request for host access, a not-`HOST_VISIBLE` memory type can be selected + if it may improve performance. + + By using this flag, you declare that you will check if the allocation ended up in a `HOST_VISIBLE` memory type + (e.g. using vmaGetAllocationMemoryProperties()) and if not, you will create some "staging" buffer and + issue an explicit transfer to write/read your data. + To prepare for this possibility, don't forget to add appropriate flags like + `VK_BUFFER_USAGE_TRANSFER_DST_BIT`, `VK_BUFFER_USAGE_TRANSFER_SRC_BIT` to the parameters of created buffer or image. + */ + VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT = 0x00001000, + /** Allocation strategy that chooses smallest possible free range for the allocation + to minimize memory usage and fragmentation, possibly at the expense of allocation time. + */ + VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = 0x00010000, + /** Allocation strategy that chooses first suitable free range for the allocation - + not necessarily in terms of the smallest offset but the one that is easiest and fastest to find + to minimize allocation time, possibly at the expense of allocation quality. + */ + VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = 0x00020000, + /** Allocation strategy that chooses always the lowest offset in available space. + This is not the most efficient strategy but achieves highly packed data. + Used internally by defragmentation, not recommended in typical usage. + */ + VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT = 0x00040000, + /** Alias to #VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT. + */ + VMA_ALLOCATION_CREATE_STRATEGY_BEST_FIT_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT, + /** Alias to #VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT. + */ + VMA_ALLOCATION_CREATE_STRATEGY_FIRST_FIT_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT, + /** A bit mask to extract only `STRATEGY` bits from entire set of flags. + */ + VMA_ALLOCATION_CREATE_STRATEGY_MASK = + VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT | + VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT | + VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT, + + VMA_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaAllocationCreateFlagBits; +/// See #VmaAllocationCreateFlagBits. +typedef VkFlags VmaAllocationCreateFlags; + +/// Flags to be passed as VmaPoolCreateInfo::flags. +typedef enum VmaPoolCreateFlagBits +{ + /** \brief Use this flag if you always allocate only buffers and linear images or only optimal images out of this pool and so Buffer-Image Granularity can be ignored. + + This is an optional optimization flag. + + If you always allocate using vmaCreateBuffer(), vmaCreateImage(), + vmaAllocateMemoryForBuffer(), then you don't need to use it because allocator + knows exact type of your allocations so it can handle Buffer-Image Granularity + in the optimal way. + + If you also allocate using vmaAllocateMemoryForImage() or vmaAllocateMemory(), + exact type of such allocations is not known, so allocator must be conservative + in handling Buffer-Image Granularity, which can lead to suboptimal allocation + (wasted memory). In that case, if you can make sure you always allocate only + buffers and linear images or only optimal images out of this pool, use this flag + to make allocator disregard Buffer-Image Granularity and so make allocations + faster and more optimal. + */ + VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT = 0x00000002, + + /** \brief Enables alternative, linear allocation algorithm in this pool. + + Specify this flag to enable linear allocation algorithm, which always creates + new allocations after last one and doesn't reuse space from allocations freed in + between. It trades memory consumption for simplified algorithm and data + structure, which has better performance and uses less memory for metadata. + + By using this flag, you can achieve behavior of free-at-once, stack, + ring buffer, and double stack. + For details, see documentation chapter \ref linear_algorithm. + */ + VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT = 0x00000004, + + /** Bit mask to extract only `ALGORITHM` bits from entire set of flags. + */ + VMA_POOL_CREATE_ALGORITHM_MASK = + VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT, + + VMA_POOL_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaPoolCreateFlagBits; +/// Flags to be passed as VmaPoolCreateInfo::flags. See #VmaPoolCreateFlagBits. +typedef VkFlags VmaPoolCreateFlags; + +/// Flags to be passed as VmaDefragmentationInfo::flags. +typedef enum VmaDefragmentationFlagBits +{ + /* \brief Use simple but fast algorithm for defragmentation. + May not achieve best results but will require least time to compute and least allocations to copy. + */ + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT = 0x1, + /* \brief Default defragmentation algorithm, applied also when no `ALGORITHM` flag is specified. + Offers a balance between defragmentation quality and the amount of allocations and bytes that need to be moved. + */ + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT = 0x2, + /* \brief Perform full defragmentation of memory. + Can result in notably more time to compute and allocations to copy, but will achieve best memory packing. + */ + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT = 0x4, + /** \brief Use the most roboust algorithm at the cost of time to compute and number of copies to make. + Only available when bufferImageGranularity is greater than 1, since it aims to reduce + alignment issues between different types of resources. + Otherwise falls back to same behavior as #VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT. + */ + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT = 0x8, + + /// A bit mask to extract only `ALGORITHM` bits from entire set of flags. + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_MASK = + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT | + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT | + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT | + VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT, + + VMA_DEFRAGMENTATION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaDefragmentationFlagBits; +/// See #VmaDefragmentationFlagBits. +typedef VkFlags VmaDefragmentationFlags; + +/// Operation performed on single defragmentation move. See structure #VmaDefragmentationMove. +typedef enum VmaDefragmentationMoveOperation +{ + /// Buffer/image has been recreated at `dstTmpAllocation`, data has been copied, old buffer/image has been destroyed. `srcAllocation` should be changed to point to the new place. This is the default value set by vmaBeginDefragmentationPass(). + VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY = 0, + /// Set this value if you cannot move the allocation. New place reserved at `dstTmpAllocation` will be freed. `srcAllocation` will remain unchanged. + VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE = 1, + /// Set this value if you decide to abandon the allocation and you destroyed the buffer/image. New place reserved at `dstTmpAllocation` will be freed, along with `srcAllocation`, which will be destroyed. + VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY = 2, +} VmaDefragmentationMoveOperation; + +/** @} */ + +/** +\addtogroup group_virtual +@{ +*/ + +/// Flags to be passed as VmaVirtualBlockCreateInfo::flags. +typedef enum VmaVirtualBlockCreateFlagBits +{ + /** \brief Enables alternative, linear allocation algorithm in this virtual block. + + Specify this flag to enable linear allocation algorithm, which always creates + new allocations after last one and doesn't reuse space from allocations freed in + between. It trades memory consumption for simplified algorithm and data + structure, which has better performance and uses less memory for metadata. + + By using this flag, you can achieve behavior of free-at-once, stack, + ring buffer, and double stack. + For details, see documentation chapter \ref linear_algorithm. + */ + VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT = 0x00000001, + + /** \brief Bit mask to extract only `ALGORITHM` bits from entire set of flags. + */ + VMA_VIRTUAL_BLOCK_CREATE_ALGORITHM_MASK = + VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT, + + VMA_VIRTUAL_BLOCK_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaVirtualBlockCreateFlagBits; +/// Flags to be passed as VmaVirtualBlockCreateInfo::flags. See #VmaVirtualBlockCreateFlagBits. +typedef VkFlags VmaVirtualBlockCreateFlags; + +/// Flags to be passed as VmaVirtualAllocationCreateInfo::flags. +typedef enum VmaVirtualAllocationCreateFlagBits +{ + /** \brief Allocation will be created from upper stack in a double stack pool. + + This flag is only allowed for virtual blocks created with #VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT flag. + */ + VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT = VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT, + /** \brief Allocation strategy that tries to minimize memory usage. + */ + VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT, + /** \brief Allocation strategy that tries to minimize allocation time. + */ + VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT, + /** Allocation strategy that chooses always the lowest offset in available space. + This is not the most efficient strategy but achieves highly packed data. + */ + VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT = VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT, + /** \brief A bit mask to extract only `STRATEGY` bits from entire set of flags. + + These strategy flags are binary compatible with equivalent flags in #VmaAllocationCreateFlagBits. + */ + VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK = VMA_ALLOCATION_CREATE_STRATEGY_MASK, + + VMA_VIRTUAL_ALLOCATION_CREATE_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF +} VmaVirtualAllocationCreateFlagBits; +/// Flags to be passed as VmaVirtualAllocationCreateInfo::flags. See #VmaVirtualAllocationCreateFlagBits. +typedef VkFlags VmaVirtualAllocationCreateFlags; + +/** @} */ + +#endif // _VMA_ENUM_DECLARATIONS + +#ifndef _VMA_DATA_TYPES_DECLARATIONS + +/** +\addtogroup group_init +@{ */ + +/** \struct VmaAllocator +\brief Represents main object of this library initialized. + +Fill structure #VmaAllocatorCreateInfo and call function vmaCreateAllocator() to create it. +Call function vmaDestroyAllocator() to destroy it. + +It is recommended to create just one object of this type per `VkDevice` object, +right after Vulkan is initialized and keep it alive until before Vulkan device is destroyed. +*/ +VK_DEFINE_HANDLE(VmaAllocator) + +/** @} */ + +/** +\addtogroup group_alloc +@{ +*/ + +/** \struct VmaPool +\brief Represents custom memory pool + +Fill structure VmaPoolCreateInfo and call function vmaCreatePool() to create it. +Call function vmaDestroyPool() to destroy it. + +For more information see [Custom memory pools](@ref choosing_memory_type_custom_memory_pools). +*/ +VK_DEFINE_HANDLE(VmaPool) + +/** \struct VmaAllocation +\brief Represents single memory allocation. + +It may be either dedicated block of `VkDeviceMemory` or a specific region of a bigger block of this type +plus unique offset. + +There are multiple ways to create such object. +You need to fill structure VmaAllocationCreateInfo. +For more information see [Choosing memory type](@ref choosing_memory_type). + +Although the library provides convenience functions that create Vulkan buffer or image, +allocate memory for it and bind them together, +binding of the allocation to a buffer or an image is out of scope of the allocation itself. +Allocation object can exist without buffer/image bound, +binding can be done manually by the user, and destruction of it can be done +independently of destruction of the allocation. + +The object also remembers its size and some other information. +To retrieve this information, use function vmaGetAllocationInfo() and inspect +returned structure VmaAllocationInfo. +*/ +VK_DEFINE_HANDLE(VmaAllocation) + +/** \struct VmaDefragmentationContext +\brief An opaque object that represents started defragmentation process. + +Fill structure #VmaDefragmentationInfo and call function vmaBeginDefragmentation() to create it. +Call function vmaEndDefragmentation() to destroy it. +*/ +VK_DEFINE_HANDLE(VmaDefragmentationContext) + +/** @} */ + +/** +\addtogroup group_virtual +@{ +*/ + +/** \struct VmaVirtualAllocation +\brief Represents single memory allocation done inside VmaVirtualBlock. + +Use it as a unique identifier to virtual allocation within the single block. + +Use value `VK_NULL_HANDLE` to represent a null/invalid allocation. +*/ +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VmaVirtualAllocation) + +/** @} */ + +/** +\addtogroup group_virtual +@{ +*/ + +/** \struct VmaVirtualBlock +\brief Handle to a virtual block object that allows to use core allocation algorithm without allocating any real GPU memory. + +Fill in #VmaVirtualBlockCreateInfo structure and use vmaCreateVirtualBlock() to create it. Use vmaDestroyVirtualBlock() to destroy it. +For more information, see documentation chapter \ref virtual_allocator. + +This object is not thread-safe - should not be used from multiple threads simultaneously, must be synchronized externally. +*/ +VK_DEFINE_HANDLE(VmaVirtualBlock) + +/** @} */ + +/** +\addtogroup group_init +@{ +*/ + +/// Callback function called after successful vkAllocateMemory. +typedef void (VKAPI_PTR* PFN_vmaAllocateDeviceMemoryFunction)( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t memoryType, + VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory, + VkDeviceSize size, + void* VMA_NULLABLE pUserData); + +/// Callback function called before vkFreeMemory. +typedef void (VKAPI_PTR* PFN_vmaFreeDeviceMemoryFunction)( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t memoryType, + VkDeviceMemory VMA_NOT_NULL_NON_DISPATCHABLE memory, + VkDeviceSize size, + void* VMA_NULLABLE pUserData); + +/** \brief Set of callbacks that the library will call for `vkAllocateMemory` and `vkFreeMemory`. + +Provided for informative purpose, e.g. to gather statistics about number of +allocations or total amount of memory allocated in Vulkan. + +Used in VmaAllocatorCreateInfo::pDeviceMemoryCallbacks. +*/ +typedef struct VmaDeviceMemoryCallbacks +{ + /// Optional, can be null. + PFN_vmaAllocateDeviceMemoryFunction VMA_NULLABLE pfnAllocate; + /// Optional, can be null. + PFN_vmaFreeDeviceMemoryFunction VMA_NULLABLE pfnFree; + /// Optional, can be null. + void* VMA_NULLABLE pUserData; +} VmaDeviceMemoryCallbacks; + +/** \brief Pointers to some Vulkan functions - a subset used by the library. + +Used in VmaAllocatorCreateInfo::pVulkanFunctions. +*/ +typedef struct VmaVulkanFunctions +{ + /// Required when using VMA_DYNAMIC_VULKAN_FUNCTIONS. + PFN_vkGetInstanceProcAddr VMA_NULLABLE vkGetInstanceProcAddr; + /// Required when using VMA_DYNAMIC_VULKAN_FUNCTIONS. + PFN_vkGetDeviceProcAddr VMA_NULLABLE vkGetDeviceProcAddr; + PFN_vkGetPhysicalDeviceProperties VMA_NULLABLE vkGetPhysicalDeviceProperties; + PFN_vkGetPhysicalDeviceMemoryProperties VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties; + PFN_vkAllocateMemory VMA_NULLABLE vkAllocateMemory; + PFN_vkFreeMemory VMA_NULLABLE vkFreeMemory; + PFN_vkMapMemory VMA_NULLABLE vkMapMemory; + PFN_vkUnmapMemory VMA_NULLABLE vkUnmapMemory; + PFN_vkFlushMappedMemoryRanges VMA_NULLABLE vkFlushMappedMemoryRanges; + PFN_vkInvalidateMappedMemoryRanges VMA_NULLABLE vkInvalidateMappedMemoryRanges; + PFN_vkBindBufferMemory VMA_NULLABLE vkBindBufferMemory; + PFN_vkBindImageMemory VMA_NULLABLE vkBindImageMemory; + PFN_vkGetBufferMemoryRequirements VMA_NULLABLE vkGetBufferMemoryRequirements; + PFN_vkGetImageMemoryRequirements VMA_NULLABLE vkGetImageMemoryRequirements; + PFN_vkCreateBuffer VMA_NULLABLE vkCreateBuffer; + PFN_vkDestroyBuffer VMA_NULLABLE vkDestroyBuffer; + PFN_vkCreateImage VMA_NULLABLE vkCreateImage; + PFN_vkDestroyImage VMA_NULLABLE vkDestroyImage; + PFN_vkCmdCopyBuffer VMA_NULLABLE vkCmdCopyBuffer; +#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + /// Fetch "vkGetBufferMemoryRequirements2" on Vulkan >= 1.1, fetch "vkGetBufferMemoryRequirements2KHR" when using VK_KHR_dedicated_allocation extension. + PFN_vkGetBufferMemoryRequirements2KHR VMA_NULLABLE vkGetBufferMemoryRequirements2KHR; + /// Fetch "vkGetImageMemoryRequirements2" on Vulkan >= 1.1, fetch "vkGetImageMemoryRequirements2KHR" when using VK_KHR_dedicated_allocation extension. + PFN_vkGetImageMemoryRequirements2KHR VMA_NULLABLE vkGetImageMemoryRequirements2KHR; +#endif +#if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000 + /// Fetch "vkBindBufferMemory2" on Vulkan >= 1.1, fetch "vkBindBufferMemory2KHR" when using VK_KHR_bind_memory2 extension. + PFN_vkBindBufferMemory2KHR VMA_NULLABLE vkBindBufferMemory2KHR; + /// Fetch "vkBindImageMemory2" on Vulkan >= 1.1, fetch "vkBindImageMemory2KHR" when using VK_KHR_bind_memory2 extension. + PFN_vkBindImageMemory2KHR VMA_NULLABLE vkBindImageMemory2KHR; +#endif +#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000 + PFN_vkGetPhysicalDeviceMemoryProperties2KHR VMA_NULLABLE vkGetPhysicalDeviceMemoryProperties2KHR; +#endif +#if VMA_VULKAN_VERSION >= 1003000 + /// Fetch from "vkGetDeviceBufferMemoryRequirements" on Vulkan >= 1.3, but you can also fetch it from "vkGetDeviceBufferMemoryRequirementsKHR" if you enabled extension VK_KHR_maintenance4. + PFN_vkGetDeviceBufferMemoryRequirements VMA_NULLABLE vkGetDeviceBufferMemoryRequirements; + /// Fetch from "vkGetDeviceImageMemoryRequirements" on Vulkan >= 1.3, but you can also fetch it from "vkGetDeviceImageMemoryRequirementsKHR" if you enabled extension VK_KHR_maintenance4. + PFN_vkGetDeviceImageMemoryRequirements VMA_NULLABLE vkGetDeviceImageMemoryRequirements; +#endif +} VmaVulkanFunctions; + +/// Description of a Allocator to be created. +typedef struct VmaAllocatorCreateInfo +{ + /// Flags for created allocator. Use #VmaAllocatorCreateFlagBits enum. + VmaAllocatorCreateFlags flags; + /// Vulkan physical device. + /** It must be valid throughout whole lifetime of created allocator. */ + VkPhysicalDevice VMA_NOT_NULL physicalDevice; + /// Vulkan device. + /** It must be valid throughout whole lifetime of created allocator. */ + VkDevice VMA_NOT_NULL device; + /// Preferred size of a single `VkDeviceMemory` block to be allocated from large heaps > 1 GiB. Optional. + /** Set to 0 to use default, which is currently 256 MiB. */ + VkDeviceSize preferredLargeHeapBlockSize; + /// Custom CPU memory allocation callbacks. Optional. + /** Optional, can be null. When specified, will also be used for all CPU-side memory allocations. */ + const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks; + /// Informative callbacks for `vkAllocateMemory`, `vkFreeMemory`. Optional. + /** Optional, can be null. */ + const VmaDeviceMemoryCallbacks* VMA_NULLABLE pDeviceMemoryCallbacks; + /** \brief Either null or a pointer to an array of limits on maximum number of bytes that can be allocated out of particular Vulkan memory heap. + + If not NULL, it must be a pointer to an array of + `VkPhysicalDeviceMemoryProperties::memoryHeapCount` elements, defining limit on + maximum number of bytes that can be allocated out of particular Vulkan memory + heap. + + Any of the elements may be equal to `VK_WHOLE_SIZE`, which means no limit on that + heap. This is also the default in case of `pHeapSizeLimit` = NULL. + + If there is a limit defined for a heap: + + - If user tries to allocate more memory from that heap using this allocator, + the allocation fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`. + - If the limit is smaller than heap size reported in `VkMemoryHeap::size`, the + value of this limit will be reported instead when using vmaGetMemoryProperties(). + + Warning! Using this feature may not be equivalent to installing a GPU with + smaller amount of memory, because graphics driver doesn't necessary fail new + allocations with `VK_ERROR_OUT_OF_DEVICE_MEMORY` result when memory capacity is + exceeded. It may return success and just silently migrate some device memory + blocks to system RAM. This driver behavior can also be controlled using + VK_AMD_memory_overallocation_behavior extension. + */ + const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pHeapSizeLimit; + + /** \brief Pointers to Vulkan functions. Can be null. + + For details see [Pointers to Vulkan functions](@ref config_Vulkan_functions). + */ + const VmaVulkanFunctions* VMA_NULLABLE pVulkanFunctions; + /** \brief Handle to Vulkan instance object. + + Starting from version 3.0.0 this member is no longer optional, it must be set! + */ + VkInstance VMA_NOT_NULL instance; + /** \brief Optional. The highest version of Vulkan that the application is designed to use. + + It must be a value in the format as created by macro `VK_MAKE_VERSION` or a constant like: `VK_API_VERSION_1_1`, `VK_API_VERSION_1_0`. + The patch version number specified is ignored. Only the major and minor versions are considered. + It must be less or equal (preferably equal) to value as passed to `vkCreateInstance` as `VkApplicationInfo::apiVersion`. + Only versions 1.0, 1.1, 1.2, 1.3 are supported by the current implementation. + Leaving it initialized to zero is equivalent to `VK_API_VERSION_1_0`. + */ + uint32_t vulkanApiVersion; +#if VMA_EXTERNAL_MEMORY + /** \brief Either null or a pointer to an array of external memory handle types for each Vulkan memory type. + + If not NULL, it must be a pointer to an array of `VkPhysicalDeviceMemoryProperties::memoryTypeCount` + elements, defining external memory handle types of particular Vulkan memory type, + to be passed using `VkExportMemoryAllocateInfoKHR`. + + Any of the elements may be equal to 0, which means not to use `VkExportMemoryAllocateInfoKHR` on this memory type. + This is also the default in case of `pTypeExternalMemoryHandleTypes` = NULL. + */ + const VkExternalMemoryHandleTypeFlagsKHR* VMA_NULLABLE VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryTypeCount") pTypeExternalMemoryHandleTypes; +#endif // #if VMA_EXTERNAL_MEMORY +} VmaAllocatorCreateInfo; + +/// Information about existing #VmaAllocator object. +typedef struct VmaAllocatorInfo +{ + /** \brief Handle to Vulkan instance object. + + This is the same value as has been passed through VmaAllocatorCreateInfo::instance. + */ + VkInstance VMA_NOT_NULL instance; + /** \brief Handle to Vulkan physical device object. + + This is the same value as has been passed through VmaAllocatorCreateInfo::physicalDevice. + */ + VkPhysicalDevice VMA_NOT_NULL physicalDevice; + /** \brief Handle to Vulkan device object. + + This is the same value as has been passed through VmaAllocatorCreateInfo::device. + */ + VkDevice VMA_NOT_NULL device; +} VmaAllocatorInfo; + +/** @} */ + +/** +\addtogroup group_stats +@{ +*/ + +/** \brief Calculated statistics of memory usage e.g. in a specific memory type, heap, custom pool, or total. + +These are fast to calculate. +See functions: vmaGetHeapBudgets(), vmaGetPoolStatistics(). +*/ +typedef struct VmaStatistics +{ + /** \brief Number of `VkDeviceMemory` objects - Vulkan memory blocks allocated. + */ + uint32_t blockCount; + /** \brief Number of #VmaAllocation objects allocated. + + Dedicated allocations have their own blocks, so each one adds 1 to `allocationCount` as well as `blockCount`. + */ + uint32_t allocationCount; + /** \brief Number of bytes allocated in `VkDeviceMemory` blocks. + + \note To avoid confusion, please be aware that what Vulkan calls an "allocation" - a whole `VkDeviceMemory` object + (e.g. as in `VkPhysicalDeviceLimits::maxMemoryAllocationCount`) is called a "block" in VMA, while VMA calls + "allocation" a #VmaAllocation object that represents a memory region sub-allocated from such block, usually for a single buffer or image. + */ + VkDeviceSize blockBytes; + /** \brief Total number of bytes occupied by all #VmaAllocation objects. + + Always less or equal than `blockBytes`. + Difference `(blockBytes - allocationBytes)` is the amount of memory allocated from Vulkan + but unused by any #VmaAllocation. + */ + VkDeviceSize allocationBytes; +} VmaStatistics; + +/** \brief More detailed statistics than #VmaStatistics. + +These are slower to calculate. Use for debugging purposes. +See functions: vmaCalculateStatistics(), vmaCalculatePoolStatistics(). + +Previous version of the statistics API provided averages, but they have been removed +because they can be easily calculated as: + +\code +VkDeviceSize allocationSizeAvg = detailedStats.statistics.allocationBytes / detailedStats.statistics.allocationCount; +VkDeviceSize unusedBytes = detailedStats.statistics.blockBytes - detailedStats.statistics.allocationBytes; +VkDeviceSize unusedRangeSizeAvg = unusedBytes / detailedStats.unusedRangeCount; +\endcode +*/ +typedef struct VmaDetailedStatistics +{ + /// Basic statistics. + VmaStatistics statistics; + /// Number of free ranges of memory between allocations. + uint32_t unusedRangeCount; + /// Smallest allocation size. `VK_WHOLE_SIZE` if there are 0 allocations. + VkDeviceSize allocationSizeMin; + /// Largest allocation size. 0 if there are 0 allocations. + VkDeviceSize allocationSizeMax; + /// Smallest empty range size. `VK_WHOLE_SIZE` if there are 0 empty ranges. + VkDeviceSize unusedRangeSizeMin; + /// Largest empty range size. 0 if there are 0 empty ranges. + VkDeviceSize unusedRangeSizeMax; +} VmaDetailedStatistics; + +/** \brief General statistics from current state of the Allocator - +total memory usage across all memory heaps and types. + +These are slower to calculate. Use for debugging purposes. +See function vmaCalculateStatistics(). +*/ +typedef struct VmaTotalStatistics +{ + VmaDetailedStatistics memoryType[VK_MAX_MEMORY_TYPES]; + VmaDetailedStatistics memoryHeap[VK_MAX_MEMORY_HEAPS]; + VmaDetailedStatistics total; +} VmaTotalStatistics; + +/** \brief Statistics of current memory usage and available budget for a specific memory heap. + +These are fast to calculate. +See function vmaGetHeapBudgets(). +*/ +typedef struct VmaBudget +{ + /** \brief Statistics fetched from the library. + */ + VmaStatistics statistics; + /** \brief Estimated current memory usage of the program, in bytes. + + Fetched from system using VK_EXT_memory_budget extension if enabled. + + It might be different than `statistics.blockBytes` (usually higher) due to additional implicit objects + also occupying the memory, like swapchain, pipelines, descriptor heaps, command buffers, or + `VkDeviceMemory` blocks allocated outside of this library, if any. + */ + VkDeviceSize usage; + /** \brief Estimated amount of memory available to the program, in bytes. + + Fetched from system using VK_EXT_memory_budget extension if enabled. + + It might be different (most probably smaller) than `VkMemoryHeap::size[heapIndex]` due to factors + external to the program, decided by the operating system. + Difference `budget - usage` is the amount of additional memory that can probably + be allocated without problems. Exceeding the budget may result in various problems. + */ + VkDeviceSize budget; +} VmaBudget; + +/** @} */ + +/** +\addtogroup group_alloc +@{ +*/ + +/** \brief Parameters of new #VmaAllocation. + +To be used with functions like vmaCreateBuffer(), vmaCreateImage(), and many others. +*/ +typedef struct VmaAllocationCreateInfo +{ + /// Use #VmaAllocationCreateFlagBits enum. + VmaAllocationCreateFlags flags; + /** \brief Intended usage of memory. + + You can leave #VMA_MEMORY_USAGE_UNKNOWN if you specify memory requirements in other way. \n + If `pool` is not null, this member is ignored. + */ + VmaMemoryUsage usage; + /** \brief Flags that must be set in a Memory Type chosen for an allocation. + + Leave 0 if you specify memory requirements in other way. \n + If `pool` is not null, this member is ignored.*/ + VkMemoryPropertyFlags requiredFlags; + /** \brief Flags that preferably should be set in a memory type chosen for an allocation. + + Set to 0 if no additional flags are preferred. \n + If `pool` is not null, this member is ignored. */ + VkMemoryPropertyFlags preferredFlags; + /** \brief Bitmask containing one bit set for every memory type acceptable for this allocation. + + Value 0 is equivalent to `UINT32_MAX` - it means any memory type is accepted if + it meets other requirements specified by this structure, with no further + restrictions on memory type index. \n + If `pool` is not null, this member is ignored. + */ + uint32_t memoryTypeBits; + /** \brief Pool that this allocation should be created in. + + Leave `VK_NULL_HANDLE` to allocate from default pool. If not null, members: + `usage`, `requiredFlags`, `preferredFlags`, `memoryTypeBits` are ignored. + */ + VmaPool VMA_NULLABLE pool; + /** \brief Custom general-purpose pointer that will be stored in #VmaAllocation, can be read as VmaAllocationInfo::pUserData and changed using vmaSetAllocationUserData(). + + If #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT is used, it must be either + null or pointer to a null-terminated string. The string will be then copied to + internal buffer, so it doesn't need to be valid after allocation call. + */ + void* VMA_NULLABLE pUserData; + /** \brief A floating-point value between 0 and 1, indicating the priority of the allocation relative to other memory allocations. + + It is used only when #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT flag was used during creation of the #VmaAllocator object + and this allocation ends up as dedicated or is explicitly forced as dedicated using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. + Otherwise, it has the priority of a memory block where it is placed and this variable is ignored. + */ + float priority; +} VmaAllocationCreateInfo; + +/// Describes parameter of created #VmaPool. +typedef struct VmaPoolCreateInfo +{ + /** \brief Vulkan memory type index to allocate this pool from. + */ + uint32_t memoryTypeIndex; + /** \brief Use combination of #VmaPoolCreateFlagBits. + */ + VmaPoolCreateFlags flags; + /** \brief Size of a single `VkDeviceMemory` block to be allocated as part of this pool, in bytes. Optional. + + Specify nonzero to set explicit, constant size of memory blocks used by this + pool. + + Leave 0 to use default and let the library manage block sizes automatically. + Sizes of particular blocks may vary. + In this case, the pool will also support dedicated allocations. + */ + VkDeviceSize blockSize; + /** \brief Minimum number of blocks to be always allocated in this pool, even if they stay empty. + + Set to 0 to have no preallocated blocks and allow the pool be completely empty. + */ + size_t minBlockCount; + /** \brief Maximum number of blocks that can be allocated in this pool. Optional. + + Set to 0 to use default, which is `SIZE_MAX`, which means no limit. + + Set to same value as VmaPoolCreateInfo::minBlockCount to have fixed amount of memory allocated + throughout whole lifetime of this pool. + */ + size_t maxBlockCount; + /** \brief A floating-point value between 0 and 1, indicating the priority of the allocations in this pool relative to other memory allocations. + + It is used only when #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT flag was used during creation of the #VmaAllocator object. + Otherwise, this variable is ignored. + */ + float priority; + /** \brief Additional minimum alignment to be used for all allocations created from this pool. Can be 0. + + Leave 0 (default) not to impose any additional alignment. If not 0, it must be a power of two. + It can be useful in cases where alignment returned by Vulkan by functions like `vkGetBufferMemoryRequirements` is not enough, + e.g. when doing interop with OpenGL. + */ + VkDeviceSize minAllocationAlignment; + /** \brief Additional `pNext` chain to be attached to `VkMemoryAllocateInfo` used for every allocation made by this pool. Optional. + + Optional, can be null. If not null, it must point to a `pNext` chain of structures that can be attached to `VkMemoryAllocateInfo`. + It can be useful for special needs such as adding `VkExportMemoryAllocateInfoKHR`. + Structures pointed by this member must remain alive and unchanged for the whole lifetime of the custom pool. + + Please note that some structures, e.g. `VkMemoryPriorityAllocateInfoEXT`, `VkMemoryDedicatedAllocateInfoKHR`, + can be attached automatically by this library when using other, more convenient of its features. + */ + void* VMA_NULLABLE pMemoryAllocateNext; +} VmaPoolCreateInfo; + +/** @} */ + +/** +\addtogroup group_alloc +@{ +*/ + +/// Parameters of #VmaAllocation objects, that can be retrieved using function vmaGetAllocationInfo(). +typedef struct VmaAllocationInfo +{ + /** \brief Memory type index that this allocation was allocated from. + + It never changes. + */ + uint32_t memoryType; + /** \brief Handle to Vulkan memory object. + + Same memory object can be shared by multiple allocations. + + It can change after the allocation is moved during \ref defragmentation. + */ + VkDeviceMemory VMA_NULLABLE_NON_DISPATCHABLE deviceMemory; + /** \brief Offset in `VkDeviceMemory` object to the beginning of this allocation, in bytes. `(deviceMemory, offset)` pair is unique to this allocation. + + You usually don't need to use this offset. If you create a buffer or an image together with the allocation using e.g. function + vmaCreateBuffer(), vmaCreateImage(), functions that operate on these resources refer to the beginning of the buffer or image, + not entire device memory block. Functions like vmaMapMemory(), vmaBindBufferMemory() also refer to the beginning of the allocation + and apply this offset automatically. + + It can change after the allocation is moved during \ref defragmentation. + */ + VkDeviceSize offset; + /** \brief Size of this allocation, in bytes. + + It never changes. + + \note Allocation size returned in this variable may be greater than the size + requested for the resource e.g. as `VkBufferCreateInfo::size`. Whole size of the + allocation is accessible for operations on memory e.g. using a pointer after + mapping with vmaMapMemory(), but operations on the resource e.g. using + `vkCmdCopyBuffer` must be limited to the size of the resource. + */ + VkDeviceSize size; + /** \brief Pointer to the beginning of this allocation as mapped data. + + If the allocation hasn't been mapped using vmaMapMemory() and hasn't been + created with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag, this value is null. + + It can change after call to vmaMapMemory(), vmaUnmapMemory(). + It can also change after the allocation is moved during \ref defragmentation. + */ + void* VMA_NULLABLE pMappedData; + /** \brief Custom general-purpose pointer that was passed as VmaAllocationCreateInfo::pUserData or set using vmaSetAllocationUserData(). + + It can change after call to vmaSetAllocationUserData() for this allocation. + */ + void* VMA_NULLABLE pUserData; + /** \brief Custom allocation name that was set with vmaSetAllocationName(). + + It can change after call to vmaSetAllocationName() for this allocation. + + Another way to set custom name is to pass it in VmaAllocationCreateInfo::pUserData with + additional flag #VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT set [DEPRECATED]. + */ + const char* VMA_NULLABLE pName; +} VmaAllocationInfo; + +/** \brief Parameters for defragmentation. + +To be used with function vmaBeginDefragmentation(). +*/ +typedef struct VmaDefragmentationInfo +{ + /// \brief Use combination of #VmaDefragmentationFlagBits. + VmaDefragmentationFlags flags; + /** \brief Custom pool to be defragmented. + + If null then default pools will undergo defragmentation process. + */ + VmaPool VMA_NULLABLE pool; + /** \brief Maximum numbers of bytes that can be copied during single pass, while moving allocations to different places. + + `0` means no limit. + */ + VkDeviceSize maxBytesPerPass; + /** \brief Maximum number of allocations that can be moved during single pass to a different place. + + `0` means no limit. + */ + uint32_t maxAllocationsPerPass; +} VmaDefragmentationInfo; + +/// Single move of an allocation to be done for defragmentation. +typedef struct VmaDefragmentationMove +{ + /// Operation to be performed on the allocation by vmaEndDefragmentationPass(). Default value is #VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY. You can modify it. + VmaDefragmentationMoveOperation operation; + /// Allocation that should be moved. + VmaAllocation VMA_NOT_NULL srcAllocation; + /** \brief Temporary allocation pointing to destination memory that will replace `srcAllocation`. + + \warning Do not store this allocation in your data structures! It exists only temporarily, for the duration of the defragmentation pass, + to be used for binding new buffer/image to the destination memory using e.g. vmaBindBufferMemory(). + vmaEndDefragmentationPass() will destroy it and make `srcAllocation` point to this memory. + */ + VmaAllocation VMA_NOT_NULL dstTmpAllocation; +} VmaDefragmentationMove; + +/** \brief Parameters for incremental defragmentation steps. + +To be used with function vmaBeginDefragmentationPass(). +*/ +typedef struct VmaDefragmentationPassMoveInfo +{ + /// Number of elements in the `pMoves` array. + uint32_t moveCount; + /** \brief Array of moves to be performed by the user in the current defragmentation pass. + + Pointer to an array of `moveCount` elements, owned by VMA, created in vmaBeginDefragmentationPass(), destroyed in vmaEndDefragmentationPass(). + + For each element, you should: + + 1. Create a new buffer/image in the place pointed by VmaDefragmentationMove::dstMemory + VmaDefragmentationMove::dstOffset. + 2. Copy data from the VmaDefragmentationMove::srcAllocation e.g. using `vkCmdCopyBuffer`, `vkCmdCopyImage`. + 3. Make sure these commands finished executing on the GPU. + 4. Destroy the old buffer/image. + + Only then you can finish defragmentation pass by calling vmaEndDefragmentationPass(). + After this call, the allocation will point to the new place in memory. + + Alternatively, if you cannot move specific allocation, you can set VmaDefragmentationMove::operation to #VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE. + + Alternatively, if you decide you want to completely remove the allocation: + + 1. Destroy its buffer/image. + 2. Set VmaDefragmentationMove::operation to #VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY. + + Then, after vmaEndDefragmentationPass() the allocation will be freed. + */ + VmaDefragmentationMove* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(moveCount) pMoves; +} VmaDefragmentationPassMoveInfo; + +/// Statistics returned for defragmentation process in function vmaEndDefragmentation(). +typedef struct VmaDefragmentationStats +{ + /// Total number of bytes that have been copied while moving allocations to different places. + VkDeviceSize bytesMoved; + /// Total number of bytes that have been released to the system by freeing empty `VkDeviceMemory` objects. + VkDeviceSize bytesFreed; + /// Number of allocations that have been moved to different places. + uint32_t allocationsMoved; + /// Number of empty `VkDeviceMemory` objects that have been released to the system. + uint32_t deviceMemoryBlocksFreed; +} VmaDefragmentationStats; + +/** @} */ + +/** +\addtogroup group_virtual +@{ +*/ + +/// Parameters of created #VmaVirtualBlock object to be passed to vmaCreateVirtualBlock(). +typedef struct VmaVirtualBlockCreateInfo +{ + /** \brief Total size of the virtual block. + + Sizes can be expressed in bytes or any units you want as long as you are consistent in using them. + For example, if you allocate from some array of structures, 1 can mean single instance of entire structure. + */ + VkDeviceSize size; + + /** \brief Use combination of #VmaVirtualBlockCreateFlagBits. + */ + VmaVirtualBlockCreateFlags flags; + + /** \brief Custom CPU memory allocation callbacks. Optional. + + Optional, can be null. When specified, they will be used for all CPU-side memory allocations. + */ + const VkAllocationCallbacks* VMA_NULLABLE pAllocationCallbacks; +} VmaVirtualBlockCreateInfo; + +/// Parameters of created virtual allocation to be passed to vmaVirtualAllocate(). +typedef struct VmaVirtualAllocationCreateInfo +{ + /** \brief Size of the allocation. + + Cannot be zero. + */ + VkDeviceSize size; + /** \brief Required alignment of the allocation. Optional. + + Must be power of two. Special value 0 has the same meaning as 1 - means no special alignment is required, so allocation can start at any offset. + */ + VkDeviceSize alignment; + /** \brief Use combination of #VmaVirtualAllocationCreateFlagBits. + */ + VmaVirtualAllocationCreateFlags flags; + /** \brief Custom pointer to be associated with the allocation. Optional. + + It can be any value and can be used for user-defined purposes. It can be fetched or changed later. + */ + void* VMA_NULLABLE pUserData; +} VmaVirtualAllocationCreateInfo; + +/// Parameters of an existing virtual allocation, returned by vmaGetVirtualAllocationInfo(). +typedef struct VmaVirtualAllocationInfo +{ + /** \brief Offset of the allocation. + + Offset at which the allocation was made. + */ + VkDeviceSize offset; + /** \brief Size of the allocation. + + Same value as passed in VmaVirtualAllocationCreateInfo::size. + */ + VkDeviceSize size; + /** \brief Custom pointer associated with the allocation. + + Same value as passed in VmaVirtualAllocationCreateInfo::pUserData or to vmaSetVirtualAllocationUserData(). + */ + void* VMA_NULLABLE pUserData; +} VmaVirtualAllocationInfo; + +/** @} */ + +#endif // _VMA_DATA_TYPES_DECLARATIONS + +#ifndef _VMA_FUNCTION_HEADERS + +/** +\addtogroup group_init +@{ +*/ + +/// Creates #VmaAllocator object. +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator( + const VmaAllocatorCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaAllocator VMA_NULLABLE* VMA_NOT_NULL pAllocator); + +/// Destroys allocator object. +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator( + VmaAllocator VMA_NULLABLE allocator); + +/** \brief Returns information about existing #VmaAllocator object - handle to Vulkan device etc. + +It might be useful if you want to keep just the #VmaAllocator handle and fetch other required handles to +`VkPhysicalDevice`, `VkDevice` etc. every time using this function. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocatorInfo* VMA_NOT_NULL pAllocatorInfo); + +/** +PhysicalDeviceProperties are fetched from physicalDevice by the allocator. +You can access it here, without fetching it again on your own. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties( + VmaAllocator VMA_NOT_NULL allocator, + const VkPhysicalDeviceProperties* VMA_NULLABLE* VMA_NOT_NULL ppPhysicalDeviceProperties); + +/** +PhysicalDeviceMemoryProperties are fetched from physicalDevice by the allocator. +You can access it here, without fetching it again on your own. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties( + VmaAllocator VMA_NOT_NULL allocator, + const VkPhysicalDeviceMemoryProperties* VMA_NULLABLE* VMA_NOT_NULL ppPhysicalDeviceMemoryProperties); + +/** +\brief Given Memory Type Index, returns Property Flags of this memory type. + +This is just a convenience function. Same information can be obtained using +vmaGetMemoryProperties(). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t memoryTypeIndex, + VkMemoryPropertyFlags* VMA_NOT_NULL pFlags); + +/** \brief Sets index of the current frame. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t frameIndex); + +/** @} */ + +/** +\addtogroup group_stats +@{ +*/ + +/** \brief Retrieves statistics from current state of the Allocator. + +This function is called "calculate" not "get" because it has to traverse all +internal data structures, so it may be quite slow. Use it for debugging purposes. +For faster but more brief statistics suitable to be called every frame or every allocation, +use vmaGetHeapBudgets(). + +Note that when using allocator from multiple threads, returned information may immediately +become outdated. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStatistics( + VmaAllocator VMA_NOT_NULL allocator, + VmaTotalStatistics* VMA_NOT_NULL pStats); + +/** \brief Retrieves information about current memory usage and budget for all memory heaps. + +\param allocator +\param[out] pBudgets Must point to array with number of elements at least equal to number of memory heaps in physical device used. + +This function is called "get" not "calculate" because it is very fast, suitable to be called +every frame or every allocation. For more detailed statistics use vmaCalculateStatistics(). + +Note that when using allocator from multiple threads, returned information may immediately +become outdated. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetHeapBudgets( + VmaAllocator VMA_NOT_NULL allocator, + VmaBudget* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL("VkPhysicalDeviceMemoryProperties::memoryHeapCount") pBudgets); + +/** @} */ + +/** +\addtogroup group_alloc +@{ +*/ + +/** +\brief Helps to find memoryTypeIndex, given memoryTypeBits and VmaAllocationCreateInfo. + +This algorithm tries to find a memory type that: + +- Is allowed by memoryTypeBits. +- Contains all the flags from pAllocationCreateInfo->requiredFlags. +- Matches intended usage. +- Has as many flags from pAllocationCreateInfo->preferredFlags as possible. + +\return Returns VK_ERROR_FEATURE_NOT_PRESENT if not found. Receiving such result +from this function or any other allocating function probably means that your +device doesn't support any memory type with requested features for the specific +type of resource you want to use it for. Please check parameters of your +resource, like image layout (OPTIMAL versus LINEAR) or mip level count. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t memoryTypeBits, + const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo, + uint32_t* VMA_NOT_NULL pMemoryTypeIndex); + +/** +\brief Helps to find memoryTypeIndex, given VkBufferCreateInfo and VmaAllocationCreateInfo. + +It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex. +It internally creates a temporary, dummy buffer that never has memory bound. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo( + VmaAllocator VMA_NOT_NULL allocator, + const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo, + const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo, + uint32_t* VMA_NOT_NULL pMemoryTypeIndex); + +/** +\brief Helps to find memoryTypeIndex, given VkImageCreateInfo and VmaAllocationCreateInfo. + +It can be useful e.g. to determine value to be used as VmaPoolCreateInfo::memoryTypeIndex. +It internally creates a temporary, dummy image that never has memory bound. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo( + VmaAllocator VMA_NOT_NULL allocator, + const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo, + const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo, + uint32_t* VMA_NOT_NULL pMemoryTypeIndex); + +/** \brief Allocates Vulkan device memory and creates #VmaPool object. + +\param allocator Allocator object. +\param pCreateInfo Parameters of pool to create. +\param[out] pPool Handle to created pool. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool( + VmaAllocator VMA_NOT_NULL allocator, + const VmaPoolCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaPool VMA_NULLABLE* VMA_NOT_NULL pPool); + +/** \brief Destroys #VmaPool object and frees Vulkan device memory. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool( + VmaAllocator VMA_NOT_NULL allocator, + VmaPool VMA_NULLABLE pool); + +/** @} */ + +/** +\addtogroup group_stats +@{ +*/ + +/** \brief Retrieves statistics of existing #VmaPool object. + +\param allocator Allocator object. +\param pool Pool object. +\param[out] pPoolStats Statistics of specified pool. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStatistics( + VmaAllocator VMA_NOT_NULL allocator, + VmaPool VMA_NOT_NULL pool, + VmaStatistics* VMA_NOT_NULL pPoolStats); + +/** \brief Retrieves detailed statistics of existing #VmaPool object. + +\param allocator Allocator object. +\param pool Pool object. +\param[out] pPoolStats Statistics of specified pool. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaCalculatePoolStatistics( + VmaAllocator VMA_NOT_NULL allocator, + VmaPool VMA_NOT_NULL pool, + VmaDetailedStatistics* VMA_NOT_NULL pPoolStats); + +/** @} */ + +/** +\addtogroup group_alloc +@{ +*/ + +/** \brief Checks magic number in margins around all allocations in given memory pool in search for corruptions. + +Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero, +`VMA_DEBUG_MARGIN` is defined to nonzero and the pool is created in memory type that is +`HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection). + +Possible return values: + +- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for specified pool. +- `VK_SUCCESS` - corruption detection has been performed and succeeded. +- `VK_ERROR_UNKNOWN` - corruption detection has been performed and found memory corruptions around one of the allocations. + `VMA_ASSERT` is also fired in that case. +- Other value: Error returned by Vulkan, e.g. memory mapping failure. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption( + VmaAllocator VMA_NOT_NULL allocator, + VmaPool VMA_NOT_NULL pool); + +/** \brief Retrieves name of a custom pool. + +After the call `ppName` is either null or points to an internally-owned null-terminated string +containing name of the pool that was previously set. The pointer becomes invalid when the pool is +destroyed or its name is changed using vmaSetPoolName(). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName( + VmaAllocator VMA_NOT_NULL allocator, + VmaPool VMA_NOT_NULL pool, + const char* VMA_NULLABLE* VMA_NOT_NULL ppName); + +/** \brief Sets name of a custom pool. + +`pName` can be either null or pointer to a null-terminated string with new name for the pool. +Function makes internal copy of the string, so it can be changed or freed immediately after this call. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName( + VmaAllocator VMA_NOT_NULL allocator, + VmaPool VMA_NOT_NULL pool, + const char* VMA_NULLABLE pName); + +/** \brief General purpose memory allocation. + +\param allocator +\param pVkMemoryRequirements +\param pCreateInfo +\param[out] pAllocation Handle to allocated memory. +\param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo(). + +You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages(). + +It is recommended to use vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(), +vmaCreateBuffer(), vmaCreateImage() instead whenever possible. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory( + VmaAllocator VMA_NOT_NULL allocator, + const VkMemoryRequirements* VMA_NOT_NULL pVkMemoryRequirements, + const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation, + VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); + +/** \brief General purpose memory allocation for multiple allocation objects at once. + +\param allocator Allocator object. +\param pVkMemoryRequirements Memory requirements for each allocation. +\param pCreateInfo Creation parameters for each allocation. +\param allocationCount Number of allocations to make. +\param[out] pAllocations Pointer to array that will be filled with handles to created allocations. +\param[out] pAllocationInfo Optional. Pointer to array that will be filled with parameters of created allocations. + +You should free the memory using vmaFreeMemory() or vmaFreeMemoryPages(). + +Word "pages" is just a suggestion to use this function to allocate pieces of memory needed for sparse binding. +It is just a general purpose allocation function able to make multiple allocations at once. +It may be internally optimized to be more efficient than calling vmaAllocateMemory() `allocationCount` times. + +All allocations are made using same parameters. All of them are created out of the same memory pool and type. +If any allocation fails, all allocations already made within this function call are also freed, so that when +returned result is not `VK_SUCCESS`, `pAllocation` array is always entirely filled with `VK_NULL_HANDLE`. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages( + VmaAllocator VMA_NOT_NULL allocator, + const VkMemoryRequirements* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pVkMemoryRequirements, + const VmaAllocationCreateInfo* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pCreateInfo, + size_t allocationCount, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations, + VmaAllocationInfo* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) pAllocationInfo); + +/** \brief Allocates memory suitable for given `VkBuffer`. + +\param allocator +\param buffer +\param pCreateInfo +\param[out] pAllocation Handle to allocated memory. +\param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo(). + +It only creates #VmaAllocation. To bind the memory to the buffer, use vmaBindBufferMemory(). + +This is a special-purpose function. In most cases you should use vmaCreateBuffer(). + +You must free the allocation using vmaFreeMemory() when no longer needed. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer( + VmaAllocator VMA_NOT_NULL allocator, + VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer, + const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation, + VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); + +/** \brief Allocates memory suitable for given `VkImage`. + +\param allocator +\param image +\param pCreateInfo +\param[out] pAllocation Handle to allocated memory. +\param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo(). + +It only creates #VmaAllocation. To bind the memory to the buffer, use vmaBindImageMemory(). + +This is a special-purpose function. In most cases you should use vmaCreateImage(). + +You must free the allocation using vmaFreeMemory() when no longer needed. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage( + VmaAllocator VMA_NOT_NULL allocator, + VkImage VMA_NOT_NULL_NON_DISPATCHABLE image, + const VmaAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation, + VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); + +/** \brief Frees memory previously allocated using vmaAllocateMemory(), vmaAllocateMemoryForBuffer(), or vmaAllocateMemoryForImage(). + +Passing `VK_NULL_HANDLE` as `allocation` is valid. Such function call is just skipped. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory( + VmaAllocator VMA_NOT_NULL allocator, + const VmaAllocation VMA_NULLABLE allocation); + +/** \brief Frees memory and destroys multiple allocations. + +Word "pages" is just a suggestion to use this function to free pieces of memory used for sparse binding. +It is just a general purpose function to free memory and destroy allocations made using e.g. vmaAllocateMemory(), +vmaAllocateMemoryPages() and other functions. +It may be internally optimized to be more efficient than calling vmaFreeMemory() `allocationCount` times. + +Allocations in `pAllocations` array can come from any memory pools and types. +Passing `VK_NULL_HANDLE` as elements of `pAllocations` array is valid. Such entries are just skipped. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages( + VmaAllocator VMA_NOT_NULL allocator, + size_t allocationCount, + const VmaAllocation VMA_NULLABLE* VMA_NOT_NULL VMA_LEN_IF_NOT_NULL(allocationCount) pAllocations); + +/** \brief Returns current information about specified allocation. + +Current parameters of given allocation are returned in `pAllocationInfo`. + +Although this function doesn't lock any mutex, so it should be quite efficient, +you should avoid calling it too often. +You can retrieve same VmaAllocationInfo structure while creating your resource, from function +vmaCreateBuffer(), vmaCreateImage(). You can remember it if you are sure parameters don't change +(e.g. due to defragmentation). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VmaAllocationInfo* VMA_NOT_NULL pAllocationInfo); + +/** \brief Sets pUserData in given allocation to new value. + +The value of pointer `pUserData` is copied to allocation's `pUserData`. +It is opaque, so you can use it however you want - e.g. +as a pointer, ordinal number or some handle to you own data. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + void* VMA_NULLABLE pUserData); + +/** \brief Sets pName in given allocation to new value. + +`pName` must be either null, or pointer to a null-terminated string. The function +makes local copy of the string and sets it as allocation's `pName`. String +passed as pName doesn't need to be valid for whole lifetime of the allocation - +you can free it after this call. String previously pointed by allocation's +`pName` is freed from memory. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationName( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + const char* VMA_NULLABLE pName); + +/** +\brief Given an allocation, returns Property Flags of its memory type. + +This is just a convenience function. Same information can be obtained using +vmaGetAllocationInfo() + vmaGetMemoryProperties(). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationMemoryProperties( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkMemoryPropertyFlags* VMA_NOT_NULL pFlags); + +/** \brief Maps memory represented by given allocation and returns pointer to it. + +Maps memory represented by given allocation to make it accessible to CPU code. +When succeeded, `*ppData` contains pointer to first byte of this memory. + +\warning +If the allocation is part of a bigger `VkDeviceMemory` block, returned pointer is +correctly offsetted to the beginning of region assigned to this particular allocation. +Unlike the result of `vkMapMemory`, it points to the allocation, not to the beginning of the whole block. +You should not add VmaAllocationInfo::offset to it! + +Mapping is internally reference-counted and synchronized, so despite raw Vulkan +function `vkMapMemory()` cannot be used to map same block of `VkDeviceMemory` +multiple times simultaneously, it is safe to call this function on allocations +assigned to the same memory block. Actual Vulkan memory will be mapped on first +mapping and unmapped on last unmapping. + +If the function succeeded, you must call vmaUnmapMemory() to unmap the +allocation when mapping is no longer needed or before freeing the allocation, at +the latest. + +It also safe to call this function multiple times on the same allocation. You +must call vmaUnmapMemory() same number of times as you called vmaMapMemory(). + +It is also safe to call this function on allocation created with +#VMA_ALLOCATION_CREATE_MAPPED_BIT flag. Its memory stays mapped all the time. +You must still call vmaUnmapMemory() same number of times as you called +vmaMapMemory(). You must not call vmaUnmapMemory() additional time to free the +"0-th" mapping made automatically due to #VMA_ALLOCATION_CREATE_MAPPED_BIT flag. + +This function fails when used on allocation made in memory type that is not +`HOST_VISIBLE`. + +This function doesn't automatically flush or invalidate caches. +If the allocation is made from a memory types that is not `HOST_COHERENT`, +you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + void* VMA_NULLABLE* VMA_NOT_NULL ppData); + +/** \brief Unmaps memory represented by given allocation, mapped previously using vmaMapMemory(). + +For details, see description of vmaMapMemory(). + +This function doesn't automatically flush or invalidate caches. +If the allocation is made from a memory types that is not `HOST_COHERENT`, +you also need to use vmaInvalidateAllocation() / vmaFlushAllocation(), as required by Vulkan specification. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation); + +/** \brief Flushes memory of given allocation. + +Calls `vkFlushMappedMemoryRanges()` for memory associated with given range of given allocation. +It needs to be called after writing to a mapped memory for memory types that are not `HOST_COHERENT`. +Unmap operation doesn't do that automatically. + +- `offset` must be relative to the beginning of allocation. +- `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation. +- `offset` and `size` don't have to be aligned. + They are internally rounded down/up to multiply of `nonCoherentAtomSize`. +- If `size` is 0, this call is ignored. +- If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`, + this call is ignored. + +Warning! `offset` and `size` are relative to the contents of given `allocation`. +If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively. +Do not pass allocation's offset as `offset`!!! + +This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is +called, otherwise `VK_SUCCESS`. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkDeviceSize offset, + VkDeviceSize size); + +/** \brief Invalidates memory of given allocation. + +Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given range of given allocation. +It needs to be called before reading from a mapped memory for memory types that are not `HOST_COHERENT`. +Map operation doesn't do that automatically. + +- `offset` must be relative to the beginning of allocation. +- `size` can be `VK_WHOLE_SIZE`. It means all memory from `offset` the the end of given allocation. +- `offset` and `size` don't have to be aligned. + They are internally rounded down/up to multiply of `nonCoherentAtomSize`. +- If `size` is 0, this call is ignored. +- If memory type that the `allocation` belongs to is not `HOST_VISIBLE` or it is `HOST_COHERENT`, + this call is ignored. + +Warning! `offset` and `size` are relative to the contents of given `allocation`. +If you mean whole allocation, you can pass 0 and `VK_WHOLE_SIZE`, respectively. +Do not pass allocation's offset as `offset`!!! + +This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if +it is called, otherwise `VK_SUCCESS`. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkDeviceSize offset, + VkDeviceSize size); + +/** \brief Flushes memory of given set of allocations. + +Calls `vkFlushMappedMemoryRanges()` for memory associated with given ranges of given allocations. +For more information, see documentation of vmaFlushAllocation(). + +\param allocator +\param allocationCount +\param allocations +\param offsets If not null, it must point to an array of offsets of regions to flush, relative to the beginning of respective allocations. Null means all ofsets are zero. +\param sizes If not null, it must point to an array of sizes of regions to flush in respective allocations. Null means `VK_WHOLE_SIZE` for all allocations. + +This function returns the `VkResult` from `vkFlushMappedMemoryRanges` if it is +called, otherwise `VK_SUCCESS`. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t allocationCount, + const VmaAllocation VMA_NOT_NULL* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations, + const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets, + const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes); + +/** \brief Invalidates memory of given set of allocations. + +Calls `vkInvalidateMappedMemoryRanges()` for memory associated with given ranges of given allocations. +For more information, see documentation of vmaInvalidateAllocation(). + +\param allocator +\param allocationCount +\param allocations +\param offsets If not null, it must point to an array of offsets of regions to flush, relative to the beginning of respective allocations. Null means all ofsets are zero. +\param sizes If not null, it must point to an array of sizes of regions to flush in respective allocations. Null means `VK_WHOLE_SIZE` for all allocations. + +This function returns the `VkResult` from `vkInvalidateMappedMemoryRanges` if it is +called, otherwise `VK_SUCCESS`. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t allocationCount, + const VmaAllocation VMA_NOT_NULL* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) allocations, + const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) offsets, + const VkDeviceSize* VMA_NULLABLE VMA_LEN_IF_NOT_NULL(allocationCount) sizes); + +/** \brief Checks magic number in margins around all allocations in given memory types (in both default and custom pools) in search for corruptions. + +\param allocator +\param memoryTypeBits Bit mask, where each bit set means that a memory type with that index should be checked. + +Corruption detection is enabled only when `VMA_DEBUG_DETECT_CORRUPTION` macro is defined to nonzero, +`VMA_DEBUG_MARGIN` is defined to nonzero and only for memory types that are +`HOST_VISIBLE` and `HOST_COHERENT`. For more information, see [Corruption detection](@ref debugging_memory_usage_corruption_detection). + +Possible return values: + +- `VK_ERROR_FEATURE_NOT_PRESENT` - corruption detection is not enabled for any of specified memory types. +- `VK_SUCCESS` - corruption detection has been performed and succeeded. +- `VK_ERROR_UNKNOWN` - corruption detection has been performed and found memory corruptions around one of the allocations. + `VMA_ASSERT` is also fired in that case. +- Other value: Error returned by Vulkan, e.g. memory mapping failure. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption( + VmaAllocator VMA_NOT_NULL allocator, + uint32_t memoryTypeBits); + +/** \brief Begins defragmentation process. + +\param allocator Allocator object. +\param pInfo Structure filled with parameters of defragmentation. +\param[out] pContext Context object that must be passed to vmaEndDefragmentation() to finish defragmentation. +\returns +- `VK_SUCCESS` if defragmentation can begin. +- `VK_ERROR_FEATURE_NOT_PRESENT` if defragmentation is not supported. + +For more information about defragmentation, see documentation chapter: +[Defragmentation](@ref defragmentation). +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentation( + VmaAllocator VMA_NOT_NULL allocator, + const VmaDefragmentationInfo* VMA_NOT_NULL pInfo, + VmaDefragmentationContext VMA_NULLABLE* VMA_NOT_NULL pContext); + +/** \brief Ends defragmentation process. + +\param allocator Allocator object. +\param context Context object that has been created by vmaBeginDefragmentation(). +\param[out] pStats Optional stats for the defragmentation. Can be null. + +Use this function to finish defragmentation started by vmaBeginDefragmentation(). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaEndDefragmentation( + VmaAllocator VMA_NOT_NULL allocator, + VmaDefragmentationContext VMA_NOT_NULL context, + VmaDefragmentationStats* VMA_NULLABLE pStats); + +/** \brief Starts single defragmentation pass. + +\param allocator Allocator object. +\param context Context object that has been created by vmaBeginDefragmentation(). +\param[out] pPassInfo Computed information for current pass. +\returns +- `VK_SUCCESS` if no more moves are possible. Then you can omit call to vmaEndDefragmentationPass() and simply end whole defragmentation. +- `VK_INCOMPLETE` if there are pending moves returned in `pPassInfo`. You need to perform them, call vmaEndDefragmentationPass(), + and then preferably try another pass with vmaBeginDefragmentationPass(). +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass( + VmaAllocator VMA_NOT_NULL allocator, + VmaDefragmentationContext VMA_NOT_NULL context, + VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo); + +/** \brief Ends single defragmentation pass. + +\param allocator Allocator object. +\param context Context object that has been created by vmaBeginDefragmentation(). +\param pPassInfo Computed information for current pass filled by vmaBeginDefragmentationPass() and possibly modified by you. + +Returns `VK_SUCCESS` if no more moves are possible or `VK_INCOMPLETE` if more defragmentations are possible. + +Ends incremental defragmentation pass and commits all defragmentation moves from `pPassInfo`. +After this call: + +- Allocations at `pPassInfo[i].srcAllocation` that had `pPassInfo[i].operation ==` #VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY + (which is the default) will be pointing to the new destination place. +- Allocation at `pPassInfo[i].srcAllocation` that had `pPassInfo[i].operation ==` #VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY + will be freed. + +If no more moves are possible you can end whole defragmentation. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass( + VmaAllocator VMA_NOT_NULL allocator, + VmaDefragmentationContext VMA_NOT_NULL context, + VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo); + +/** \brief Binds buffer to allocation. + +Binds specified buffer to region of memory represented by specified allocation. +Gets `VkDeviceMemory` handle and offset from the allocation. +If you want to create a buffer, allocate memory for it and bind them together separately, +you should use this function for binding instead of standard `vkBindBufferMemory()`, +because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple +allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously +(which is illegal in Vulkan). + +It is recommended to use function vmaCreateBuffer() instead of this one. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer); + +/** \brief Binds buffer to allocation with additional parameters. + +\param allocator +\param allocation +\param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0. +\param buffer +\param pNext A chain of structures to be attached to `VkBindBufferMemoryInfoKHR` structure used internally. Normally it should be null. + +This function is similar to vmaBindBufferMemory(), but it provides additional parameters. + +If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag +or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkDeviceSize allocationLocalOffset, + VkBuffer VMA_NOT_NULL_NON_DISPATCHABLE buffer, + const void* VMA_NULLABLE pNext); + +/** \brief Binds image to allocation. + +Binds specified image to region of memory represented by specified allocation. +Gets `VkDeviceMemory` handle and offset from the allocation. +If you want to create an image, allocate memory for it and bind them together separately, +you should use this function for binding instead of standard `vkBindImageMemory()`, +because it ensures proper synchronization so that when a `VkDeviceMemory` object is used by multiple +allocations, calls to `vkBind*Memory()` or `vkMapMemory()` won't happen from multiple threads simultaneously +(which is illegal in Vulkan). + +It is recommended to use function vmaCreateImage() instead of this one. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkImage VMA_NOT_NULL_NON_DISPATCHABLE image); + +/** \brief Binds image to allocation with additional parameters. + +\param allocator +\param allocation +\param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the `allocation`. Normally it should be 0. +\param image +\param pNext A chain of structures to be attached to `VkBindImageMemoryInfoKHR` structure used internally. Normally it should be null. + +This function is similar to vmaBindImageMemory(), but it provides additional parameters. + +If `pNext` is not null, #VmaAllocator object must have been created with #VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT flag +or with VmaAllocatorCreateInfo::vulkanApiVersion `>= VK_API_VERSION_1_1`. Otherwise the call fails. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkDeviceSize allocationLocalOffset, + VkImage VMA_NOT_NULL_NON_DISPATCHABLE image, + const void* VMA_NULLABLE pNext); + +/** \brief Creates a new `VkBuffer`, allocates and binds memory for it. + +\param allocator +\param pBufferCreateInfo +\param pAllocationCreateInfo +\param[out] pBuffer Buffer that was created. +\param[out] pAllocation Allocation that was created. +\param[out] pAllocationInfo Optional. Information about allocated memory. It can be later fetched using function vmaGetAllocationInfo(). + +This function automatically: + +-# Creates buffer. +-# Allocates appropriate memory for it. +-# Binds the buffer with the memory. + +If any of these operations fail, buffer and allocation are not created, +returned value is negative error code, `*pBuffer` and `*pAllocation` are null. + +If the function succeeded, you must destroy both buffer and allocation when you +no longer need them using either convenience function vmaDestroyBuffer() or +separately, using `vkDestroyBuffer()` and vmaFreeMemory(). + +If #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag was used, +VK_KHR_dedicated_allocation extension is used internally to query driver whether +it requires or prefers the new buffer to have dedicated allocation. If yes, +and if dedicated allocation is possible +(#VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT is not used), it creates dedicated +allocation for this buffer, just like when using +#VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. + +\note This function creates a new `VkBuffer`. Sub-allocation of parts of one large buffer, +although recommended as a good practice, is out of scope of this library and could be implemented +by the user as a higher-level logic on top of VMA. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer( + VmaAllocator VMA_NOT_NULL allocator, + const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo, + const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo, + VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation, + VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); + +/** \brief Creates a buffer with additional minimum alignment. + +Similar to vmaCreateBuffer() but provides additional parameter `minAlignment` which allows to specify custom, +minimum alignment to be used when placing the buffer inside a larger memory block, which may be needed e.g. +for interop with OpenGL. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBufferWithAlignment( + VmaAllocator VMA_NOT_NULL allocator, + const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo, + const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo, + VkDeviceSize minAlignment, + VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation, + VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); + +/** \brief Creates a new `VkBuffer`, binds already created memory for it. + +\param allocator +\param allocation Allocation that provides memory to be used for binding new buffer to it. +\param pBufferCreateInfo +\param[out] pBuffer Buffer that was created. + +This function automatically: + +-# Creates buffer. +-# Binds the buffer with the supplied memory. + +If any of these operations fail, buffer is not created, +returned value is negative error code and `*pBuffer` is null. + +If the function succeeded, you must destroy the buffer when you +no longer need it using `vkDestroyBuffer()`. If you want to also destroy the corresponding +allocation you can use convenience function vmaDestroyBuffer(). + +\note There is a new version of this function augmented with parameter `allocationLocalOffset` - see vmaCreateAliasingBuffer2(). +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo, + VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer); + +/** \brief Creates a new `VkBuffer`, binds already created memory for it. + +\param allocator +\param allocation Allocation that provides memory to be used for binding new buffer to it. +\param allocationLocalOffset Additional offset to be added while binding, relative to the beginning of the allocation. Normally it should be 0. +\param pBufferCreateInfo +\param[out] pBuffer Buffer that was created. + +This function automatically: + +-# Creates buffer. +-# Binds the buffer with the supplied memory. + +If any of these operations fail, buffer is not created, +returned value is negative error code and `*pBuffer` is null. + +If the function succeeded, you must destroy the buffer when you +no longer need it using `vkDestroyBuffer()`. If you want to also destroy the corresponding +allocation you can use convenience function vmaDestroyBuffer(). + +\note This is a new version of the function augmented with parameter `allocationLocalOffset`. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer2( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkDeviceSize allocationLocalOffset, + const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo, + VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer); + +/** \brief Destroys Vulkan buffer and frees allocated memory. + +This is just a convenience function equivalent to: + +\code +vkDestroyBuffer(device, buffer, allocationCallbacks); +vmaFreeMemory(allocator, allocation); +\endcode + +It is safe to pass null as buffer and/or allocation. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer( + VmaAllocator VMA_NOT_NULL allocator, + VkBuffer VMA_NULLABLE_NON_DISPATCHABLE buffer, + VmaAllocation VMA_NULLABLE allocation); + +/// Function similar to vmaCreateBuffer(). +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage( + VmaAllocator VMA_NOT_NULL allocator, + const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo, + const VmaAllocationCreateInfo* VMA_NOT_NULL pAllocationCreateInfo, + VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage, + VmaAllocation VMA_NULLABLE* VMA_NOT_NULL pAllocation, + VmaAllocationInfo* VMA_NULLABLE pAllocationInfo); + +/// Function similar to vmaCreateAliasingBuffer() but for images. +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo, + VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage); + +/// Function similar to vmaCreateAliasingBuffer2() but for images. +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage2( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkDeviceSize allocationLocalOffset, + const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo, + VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage); + +/** \brief Destroys Vulkan image and frees allocated memory. + +This is just a convenience function equivalent to: + +\code +vkDestroyImage(device, image, allocationCallbacks); +vmaFreeMemory(allocator, allocation); +\endcode + +It is safe to pass null as image and/or allocation. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage( + VmaAllocator VMA_NOT_NULL allocator, + VkImage VMA_NULLABLE_NON_DISPATCHABLE image, + VmaAllocation VMA_NULLABLE allocation); + +/** @} */ + +/** +\addtogroup group_virtual +@{ +*/ + +/** \brief Creates new #VmaVirtualBlock object. + +\param pCreateInfo Parameters for creation. +\param[out] pVirtualBlock Returned virtual block object or `VMA_NULL` if creation failed. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateVirtualBlock( + const VmaVirtualBlockCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaVirtualBlock VMA_NULLABLE* VMA_NOT_NULL pVirtualBlock); + +/** \brief Destroys #VmaVirtualBlock object. + +Please note that you should consciously handle virtual allocations that could remain unfreed in the block. +You should either free them individually using vmaVirtualFree() or call vmaClearVirtualBlock() +if you are sure this is what you want. If you do neither, an assert is called. + +If you keep pointers to some additional metadata associated with your virtual allocations in their `pUserData`, +don't forget to free them. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyVirtualBlock( + VmaVirtualBlock VMA_NULLABLE virtualBlock); + +/** \brief Returns true of the #VmaVirtualBlock is empty - contains 0 virtual allocations and has all its space available for new allocations. +*/ +VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaIsVirtualBlockEmpty( + VmaVirtualBlock VMA_NOT_NULL virtualBlock); + +/** \brief Returns information about a specific virtual allocation within a virtual block, like its size and `pUserData` pointer. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualAllocationInfo( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo); + +/** \brief Allocates new virtual allocation inside given #VmaVirtualBlock. + +If the allocation fails due to not enough free space available, `VK_ERROR_OUT_OF_DEVICE_MEMORY` is returned +(despite the function doesn't ever allocate actual GPU memory). +`pAllocation` is then set to `VK_NULL_HANDLE` and `pOffset`, if not null, it set to `UINT64_MAX`. + +\param virtualBlock Virtual block +\param pCreateInfo Parameters for the allocation +\param[out] pAllocation Returned handle of the new allocation +\param[out] pOffset Returned offset of the new allocation. Optional, can be null. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaVirtualAllocate( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + const VmaVirtualAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pAllocation, + VkDeviceSize* VMA_NULLABLE pOffset); + +/** \brief Frees virtual allocation inside given #VmaVirtualBlock. + +It is correct to call this function with `allocation == VK_NULL_HANDLE` - it does nothing. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE allocation); + +/** \brief Frees all virtual allocations inside given #VmaVirtualBlock. + +You must either call this function or free each virtual allocation individually with vmaVirtualFree() +before destroying a virtual block. Otherwise, an assert is called. + +If you keep pointer to some additional metadata associated with your virtual allocation in its `pUserData`, +don't forget to free it as well. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaClearVirtualBlock( + VmaVirtualBlock VMA_NOT_NULL virtualBlock); + +/** \brief Changes custom pointer associated with given virtual allocation. +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaSetVirtualAllocationUserData( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, + void* VMA_NULLABLE pUserData); + +/** \brief Calculates and returns statistics about virtual allocations and memory usage in given #VmaVirtualBlock. + +This function is fast to call. For more detailed statistics, see vmaCalculateVirtualBlockStatistics(). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualBlockStatistics( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaStatistics* VMA_NOT_NULL pStats); + +/** \brief Calculates and returns detailed statistics about virtual allocations and memory usage in given #VmaVirtualBlock. + +This function is slow to call. Use for debugging purposes. +For less detailed statistics, see vmaGetVirtualBlockStatistics(). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaCalculateVirtualBlockStatistics( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaDetailedStatistics* VMA_NOT_NULL pStats); + +/** @} */ + +#if VMA_STATS_STRING_ENABLED +/** +\addtogroup group_stats +@{ +*/ + +/** \brief Builds and returns a null-terminated string in JSON format with information about given #VmaVirtualBlock. +\param virtualBlock Virtual block. +\param[out] ppStatsString Returned string. +\param detailedMap Pass `VK_FALSE` to only obtain statistics as returned by vmaCalculateVirtualBlockStatistics(). Pass `VK_TRUE` to also obtain full list of allocations and free spaces. + +Returned string must be freed using vmaFreeVirtualBlockStatsString(). +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaBuildVirtualBlockStatsString( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + char* VMA_NULLABLE* VMA_NOT_NULL ppStatsString, + VkBool32 detailedMap); + +/// Frees a string returned by vmaBuildVirtualBlockStatsString(). +VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString( + VmaVirtualBlock VMA_NOT_NULL virtualBlock, + char* VMA_NULLABLE pStatsString); + +/** \brief Builds and returns statistics as a null-terminated string in JSON format. +\param allocator +\param[out] ppStatsString Must be freed using vmaFreeStatsString() function. +\param detailedMap +*/ +VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString( + VmaAllocator VMA_NOT_NULL allocator, + char* VMA_NULLABLE* VMA_NOT_NULL ppStatsString, + VkBool32 detailedMap); + +VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString( + VmaAllocator VMA_NOT_NULL allocator, + char* VMA_NULLABLE pStatsString); + +/** @} */ + +#endif // VMA_STATS_STRING_ENABLED + +#endif // _VMA_FUNCTION_HEADERS + +#ifdef __cplusplus +} +#endif + +#endif // AMD_VULKAN_MEMORY_ALLOCATOR_H + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +// +// IMPLEMENTATION +// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +// For Visual Studio IntelliSense. +#if defined(__cplusplus) && defined(__INTELLISENSE__) +#define VMA_IMPLEMENTATION +#endif + +#ifdef VMA_IMPLEMENTATION +#undef VMA_IMPLEMENTATION + +#include +#include +#include +#include +#include + +#ifdef _MSC_VER + #include // For functions like __popcnt, _BitScanForward etc. +#endif +#if __cplusplus >= 202002L || _MSVC_LANG >= 202002L // C++20 + #include // For std::popcount +#endif + +/******************************************************************************* +CONFIGURATION SECTION + +Define some of these macros before each #include of this header or change them +here if you need other then default behavior depending on your environment. +*/ +#ifndef _VMA_CONFIGURATION + +/* +Define this macro to 1 to make the library fetch pointers to Vulkan functions +internally, like: + + vulkanFunctions.vkAllocateMemory = &vkAllocateMemory; +*/ +#if !defined(VMA_STATIC_VULKAN_FUNCTIONS) && !defined(VK_NO_PROTOTYPES) + #define VMA_STATIC_VULKAN_FUNCTIONS 1 +#endif + +/* +Define this macro to 1 to make the library fetch pointers to Vulkan functions +internally, like: + + vulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkGetDeviceProcAddr(device, "vkAllocateMemory"); + +To use this feature in new versions of VMA you now have to pass +VmaVulkanFunctions::vkGetInstanceProcAddr and vkGetDeviceProcAddr as +VmaAllocatorCreateInfo::pVulkanFunctions. Other members can be null. +*/ +#if !defined(VMA_DYNAMIC_VULKAN_FUNCTIONS) + #define VMA_DYNAMIC_VULKAN_FUNCTIONS 1 +#endif + +#ifndef VMA_USE_STL_SHARED_MUTEX + // Compiler conforms to C++17. + #if __cplusplus >= 201703L + #define VMA_USE_STL_SHARED_MUTEX 1 + // Visual studio defines __cplusplus properly only when passed additional parameter: /Zc:__cplusplus + // Otherwise it is always 199711L, despite shared_mutex works since Visual Studio 2015 Update 2. + #elif defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023918 && __cplusplus == 199711L && _MSVC_LANG >= 201703L + #define VMA_USE_STL_SHARED_MUTEX 1 + #else + #define VMA_USE_STL_SHARED_MUTEX 0 + #endif +#endif + +/* +Define this macro to include custom header files without having to edit this file directly, e.g.: + + // Inside of "my_vma_configuration_user_includes.h": + + #include "my_custom_assert.h" // for MY_CUSTOM_ASSERT + #include "my_custom_min.h" // for my_custom_min + #include + #include + + // Inside a different file, which includes "vk_mem_alloc.h": + + #define VMA_CONFIGURATION_USER_INCLUDES_H "my_vma_configuration_user_includes.h" + #define VMA_ASSERT(expr) MY_CUSTOM_ASSERT(expr) + #define VMA_MIN(v1, v2) (my_custom_min(v1, v2)) + #include "vk_mem_alloc.h" + ... + +The following headers are used in this CONFIGURATION section only, so feel free to +remove them if not needed. +*/ +#if !defined(VMA_CONFIGURATION_USER_INCLUDES_H) + #include // for assert + #include // for min, max + #include +#else + #include VMA_CONFIGURATION_USER_INCLUDES_H +#endif + +#ifndef VMA_NULL + // Value used as null pointer. Define it to e.g.: nullptr, NULL, 0, (void*)0. + #define VMA_NULL nullptr +#endif + +// Normal assert to check for programmer's errors, especially in Debug configuration. +#ifndef VMA_ASSERT + #ifdef NDEBUG + #define VMA_ASSERT(expr) + #else + #define VMA_ASSERT(expr) assert(expr) + #endif +#endif + +// Assert that will be called very often, like inside data structures e.g. operator[]. +// Making it non-empty can make program slow. +#ifndef VMA_HEAVY_ASSERT + #ifdef NDEBUG + #define VMA_HEAVY_ASSERT(expr) + #else + #define VMA_HEAVY_ASSERT(expr) //VMA_ASSERT(expr) + #endif +#endif + +// If your compiler is not compatible with C++17 and definition of +// aligned_alloc() function is missing, uncommenting following line may help: + +//#include + +#if defined(__ANDROID_API__) && (__ANDROID_API__ < 16) +#include +static void* vma_aligned_alloc(size_t alignment, size_t size) +{ + // alignment must be >= sizeof(void*) + if(alignment < sizeof(void*)) + { + alignment = sizeof(void*); + } + + return memalign(alignment, size); +} +#elif defined(__APPLE__) || defined(__ANDROID__) || (defined(__linux__) && defined(__GLIBCXX__) && !defined(_GLIBCXX_HAVE_ALIGNED_ALLOC)) +#include + +#if defined(__APPLE__) +#include +#endif + +static void* vma_aligned_alloc(size_t alignment, size_t size) +{ + // Unfortunately, aligned_alloc causes VMA to crash due to it returning null pointers. (At least under 11.4) + // Therefore, for now disable this specific exception until a proper solution is found. + //#if defined(__APPLE__) && (defined(MAC_OS_X_VERSION_10_16) || defined(__IPHONE_14_0)) + //#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_16 || __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_0 + // // For C++14, usr/include/malloc/_malloc.h declares aligned_alloc()) only + // // with the MacOSX11.0 SDK in Xcode 12 (which is what adds + // // MAC_OS_X_VERSION_10_16), even though the function is marked + // // available for 10.15. That is why the preprocessor checks for 10.16 but + // // the __builtin_available checks for 10.15. + // // People who use C++17 could call aligned_alloc with the 10.15 SDK already. + // if (__builtin_available(macOS 10.15, iOS 13, *)) + // return aligned_alloc(alignment, size); + //#endif + //#endif + + // alignment must be >= sizeof(void*) + if(alignment < sizeof(void*)) + { + alignment = sizeof(void*); + } + + void *pointer; + if(posix_memalign(&pointer, alignment, size) == 0) + return pointer; + return VMA_NULL; +} +#elif defined(_WIN32) +static void* vma_aligned_alloc(size_t alignment, size_t size) +{ + return _aligned_malloc(size, alignment); +} +#elif __cplusplus >= 201703L // Compiler conforms to C++17. +static void* vma_aligned_alloc(size_t alignment, size_t size) +{ + return aligned_alloc(alignment, size); +} +#else +static void* vma_aligned_alloc(size_t alignment, size_t size) +{ + VMA_ASSERT(0 && "Could not implement aligned_alloc automatically. Please enable C++17 or later in your compiler or provide custom implementation of macro VMA_SYSTEM_ALIGNED_MALLOC (and VMA_SYSTEM_ALIGNED_FREE if needed) using the API of your system."); + return VMA_NULL; +} +#endif + +#if defined(_WIN32) +static void vma_aligned_free(void* ptr) +{ + _aligned_free(ptr); +} +#else +static void vma_aligned_free(void* VMA_NULLABLE ptr) +{ + free(ptr); +} +#endif + +#ifndef VMA_ALIGN_OF + #define VMA_ALIGN_OF(type) (__alignof(type)) +#endif + +#ifndef VMA_SYSTEM_ALIGNED_MALLOC + #define VMA_SYSTEM_ALIGNED_MALLOC(size, alignment) vma_aligned_alloc((alignment), (size)) +#endif + +#ifndef VMA_SYSTEM_ALIGNED_FREE + // VMA_SYSTEM_FREE is the old name, but might have been defined by the user + #if defined(VMA_SYSTEM_FREE) + #define VMA_SYSTEM_ALIGNED_FREE(ptr) VMA_SYSTEM_FREE(ptr) + #else + #define VMA_SYSTEM_ALIGNED_FREE(ptr) vma_aligned_free(ptr) + #endif +#endif + +#ifndef VMA_COUNT_BITS_SET + // Returns number of bits set to 1 in (v) + #define VMA_COUNT_BITS_SET(v) VmaCountBitsSet(v) +#endif + +#ifndef VMA_BITSCAN_LSB + // Scans integer for index of first nonzero value from the Least Significant Bit (LSB). If mask is 0 then returns UINT8_MAX + #define VMA_BITSCAN_LSB(mask) VmaBitScanLSB(mask) +#endif + +#ifndef VMA_BITSCAN_MSB + // Scans integer for index of first nonzero value from the Most Significant Bit (MSB). If mask is 0 then returns UINT8_MAX + #define VMA_BITSCAN_MSB(mask) VmaBitScanMSB(mask) +#endif + +#ifndef VMA_MIN + #define VMA_MIN(v1, v2) ((std::min)((v1), (v2))) +#endif + +#ifndef VMA_MAX + #define VMA_MAX(v1, v2) ((std::max)((v1), (v2))) +#endif + +#ifndef VMA_SWAP + #define VMA_SWAP(v1, v2) std::swap((v1), (v2)) +#endif + +#ifndef VMA_SORT + #define VMA_SORT(beg, end, cmp) std::sort(beg, end, cmp) +#endif + +#ifndef VMA_DEBUG_LOG_FORMAT + #define VMA_DEBUG_LOG_FORMAT(format, ...) + /* + #define VMA_DEBUG_LOG_FORMAT(format, ...) do { \ + printf((format), __VA_ARGS__); \ + printf("\n"); \ + } while(false) + */ +#endif + +#ifndef VMA_DEBUG_LOG + #define VMA_DEBUG_LOG(str) VMA_DEBUG_LOG_FORMAT("%s", (str)) +#endif + +// Define this macro to 1 to enable functions: vmaBuildStatsString, vmaFreeStatsString. +#if VMA_STATS_STRING_ENABLED + static inline void VmaUint32ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint32_t num) + { + snprintf(outStr, strLen, "%u", static_cast(num)); + } + static inline void VmaUint64ToStr(char* VMA_NOT_NULL outStr, size_t strLen, uint64_t num) + { + snprintf(outStr, strLen, "%llu", static_cast(num)); + } + static inline void VmaPtrToStr(char* VMA_NOT_NULL outStr, size_t strLen, const void* ptr) + { + snprintf(outStr, strLen, "%p", ptr); + } +#endif + +#ifndef VMA_MUTEX + class VmaMutex + { + public: + void Lock() { m_Mutex.lock(); } + void Unlock() { m_Mutex.unlock(); } + bool TryLock() { return m_Mutex.try_lock(); } + private: + std::mutex m_Mutex; + }; + #define VMA_MUTEX VmaMutex +#endif + +// Read-write mutex, where "read" is shared access, "write" is exclusive access. +#ifndef VMA_RW_MUTEX + #if VMA_USE_STL_SHARED_MUTEX + // Use std::shared_mutex from C++17. + #include + class VmaRWMutex + { + public: + void LockRead() { m_Mutex.lock_shared(); } + void UnlockRead() { m_Mutex.unlock_shared(); } + bool TryLockRead() { return m_Mutex.try_lock_shared(); } + void LockWrite() { m_Mutex.lock(); } + void UnlockWrite() { m_Mutex.unlock(); } + bool TryLockWrite() { return m_Mutex.try_lock(); } + private: + std::shared_mutex m_Mutex; + }; + #define VMA_RW_MUTEX VmaRWMutex + #elif defined(_WIN32) && defined(WINVER) && WINVER >= 0x0600 + // Use SRWLOCK from WinAPI. + // Minimum supported client = Windows Vista, server = Windows Server 2008. + class VmaRWMutex + { + public: + VmaRWMutex() { InitializeSRWLock(&m_Lock); } + void LockRead() { AcquireSRWLockShared(&m_Lock); } + void UnlockRead() { ReleaseSRWLockShared(&m_Lock); } + bool TryLockRead() { return TryAcquireSRWLockShared(&m_Lock) != FALSE; } + void LockWrite() { AcquireSRWLockExclusive(&m_Lock); } + void UnlockWrite() { ReleaseSRWLockExclusive(&m_Lock); } + bool TryLockWrite() { return TryAcquireSRWLockExclusive(&m_Lock) != FALSE; } + private: + SRWLOCK m_Lock; + }; + #define VMA_RW_MUTEX VmaRWMutex + #else + // Less efficient fallback: Use normal mutex. + class VmaRWMutex + { + public: + void LockRead() { m_Mutex.Lock(); } + void UnlockRead() { m_Mutex.Unlock(); } + bool TryLockRead() { return m_Mutex.TryLock(); } + void LockWrite() { m_Mutex.Lock(); } + void UnlockWrite() { m_Mutex.Unlock(); } + bool TryLockWrite() { return m_Mutex.TryLock(); } + private: + VMA_MUTEX m_Mutex; + }; + #define VMA_RW_MUTEX VmaRWMutex + #endif // #if VMA_USE_STL_SHARED_MUTEX +#endif // #ifndef VMA_RW_MUTEX + +/* +If providing your own implementation, you need to implement a subset of std::atomic. +*/ +#ifndef VMA_ATOMIC_UINT32 + #include + #define VMA_ATOMIC_UINT32 std::atomic +#endif + +#ifndef VMA_ATOMIC_UINT64 + #include + #define VMA_ATOMIC_UINT64 std::atomic +#endif + +#ifndef VMA_DEBUG_ALWAYS_DEDICATED_MEMORY + /** + Every allocation will have its own memory block. + Define to 1 for debugging purposes only. + */ + #define VMA_DEBUG_ALWAYS_DEDICATED_MEMORY (0) +#endif + +#ifndef VMA_MIN_ALIGNMENT + /** + Minimum alignment of all allocations, in bytes. + Set to more than 1 for debugging purposes. Must be power of two. + */ + #ifdef VMA_DEBUG_ALIGNMENT // Old name + #define VMA_MIN_ALIGNMENT VMA_DEBUG_ALIGNMENT + #else + #define VMA_MIN_ALIGNMENT (1) + #endif +#endif + +#ifndef VMA_DEBUG_MARGIN + /** + Minimum margin after every allocation, in bytes. + Set nonzero for debugging purposes only. + */ + #define VMA_DEBUG_MARGIN (0) +#endif + +#ifndef VMA_DEBUG_INITIALIZE_ALLOCATIONS + /** + Define this macro to 1 to automatically fill new allocations and destroyed + allocations with some bit pattern. + */ + #define VMA_DEBUG_INITIALIZE_ALLOCATIONS (0) +#endif + +#ifndef VMA_DEBUG_DETECT_CORRUPTION + /** + Define this macro to 1 together with non-zero value of VMA_DEBUG_MARGIN to + enable writing magic value to the margin after every allocation and + validating it, so that memory corruptions (out-of-bounds writes) are detected. + */ + #define VMA_DEBUG_DETECT_CORRUPTION (0) +#endif + +#ifndef VMA_DEBUG_GLOBAL_MUTEX + /** + Set this to 1 for debugging purposes only, to enable single mutex protecting all + entry calls to the library. Can be useful for debugging multithreading issues. + */ + #define VMA_DEBUG_GLOBAL_MUTEX (0) +#endif + +#ifndef VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY + /** + Minimum value for VkPhysicalDeviceLimits::bufferImageGranularity. + Set to more than 1 for debugging purposes only. Must be power of two. + */ + #define VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY (1) +#endif + +#ifndef VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT + /* + Set this to 1 to make VMA never exceed VkPhysicalDeviceLimits::maxMemoryAllocationCount + and return error instead of leaving up to Vulkan implementation what to do in such cases. + */ + #define VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT (0) +#endif + +#ifndef VMA_SMALL_HEAP_MAX_SIZE + /// Maximum size of a memory heap in Vulkan to consider it "small". + #define VMA_SMALL_HEAP_MAX_SIZE (1024ull * 1024 * 1024) +#endif + +#ifndef VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE + /// Default size of a block allocated as single VkDeviceMemory from a "large" heap. + #define VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE (256ull * 1024 * 1024) +#endif + +/* +Mapping hysteresis is a logic that launches when vmaMapMemory/vmaUnmapMemory is called +or a persistently mapped allocation is created and destroyed several times in a row. +It keeps additional +1 mapping of a device memory block to prevent calling actual +vkMapMemory/vkUnmapMemory too many times, which may improve performance and help +tools like RenderDoc. +*/ +#ifndef VMA_MAPPING_HYSTERESIS_ENABLED + #define VMA_MAPPING_HYSTERESIS_ENABLED 1 +#endif + +#ifndef VMA_CLASS_NO_COPY + #define VMA_CLASS_NO_COPY(className) \ + private: \ + className(const className&) = delete; \ + className& operator=(const className&) = delete; +#endif + +#define VMA_VALIDATE(cond) do { if(!(cond)) { \ + VMA_ASSERT(0 && "Validation failed: " #cond); \ + return false; \ + } } while(false) + +/******************************************************************************* +END OF CONFIGURATION +*/ +#endif // _VMA_CONFIGURATION + + +static const uint8_t VMA_ALLOCATION_FILL_PATTERN_CREATED = 0xDC; +static const uint8_t VMA_ALLOCATION_FILL_PATTERN_DESTROYED = 0xEF; +// Decimal 2139416166, float NaN, little-endian binary 66 E6 84 7F. +static const uint32_t VMA_CORRUPTION_DETECTION_MAGIC_VALUE = 0x7F84E666; + +// Copy of some Vulkan definitions so we don't need to check their existence just to handle few constants. +static const uint32_t VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY = 0x00000040; +static const uint32_t VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY = 0x00000080; +static const uint32_t VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY = 0x00020000; +static const uint32_t VK_IMAGE_CREATE_DISJOINT_BIT_COPY = 0x00000200; +static const int32_t VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT_COPY = 1000158000; +static const uint32_t VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET = 0x10000000u; +static const uint32_t VMA_ALLOCATION_TRY_COUNT = 32; +static const uint32_t VMA_VENDOR_ID_AMD = 4098; + +// This one is tricky. Vulkan specification defines this code as available since +// Vulkan 1.0, but doesn't actually define it in Vulkan SDK earlier than 1.2.131. +// See pull request #207. +#define VK_ERROR_UNKNOWN_COPY ((VkResult)-13) + + +#if VMA_STATS_STRING_ENABLED +// Correspond to values of enum VmaSuballocationType. +static const char* VMA_SUBALLOCATION_TYPE_NAMES[] = +{ + "FREE", + "UNKNOWN", + "BUFFER", + "IMAGE_UNKNOWN", + "IMAGE_LINEAR", + "IMAGE_OPTIMAL", +}; +#endif + +static VkAllocationCallbacks VmaEmptyAllocationCallbacks = + { VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL, VMA_NULL }; + + +#ifndef _VMA_ENUM_DECLARATIONS + +enum VmaSuballocationType +{ + VMA_SUBALLOCATION_TYPE_FREE = 0, + VMA_SUBALLOCATION_TYPE_UNKNOWN = 1, + VMA_SUBALLOCATION_TYPE_BUFFER = 2, + VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN = 3, + VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR = 4, + VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL = 5, + VMA_SUBALLOCATION_TYPE_MAX_ENUM = 0x7FFFFFFF +}; + +enum VMA_CACHE_OPERATION +{ + VMA_CACHE_FLUSH, + VMA_CACHE_INVALIDATE +}; + +enum class VmaAllocationRequestType +{ + Normal, + TLSF, + // Used by "Linear" algorithm. + UpperAddress, + EndOf1st, + EndOf2nd, +}; + +#endif // _VMA_ENUM_DECLARATIONS + +#ifndef _VMA_FORWARD_DECLARATIONS +// Opaque handle used by allocation algorithms to identify single allocation in any conforming way. +VK_DEFINE_NON_DISPATCHABLE_HANDLE(VmaAllocHandle) + +struct VmaMutexLock; +struct VmaMutexLockRead; +struct VmaMutexLockWrite; + +template +struct AtomicTransactionalIncrement; + +template +struct VmaStlAllocator; + +template +class VmaVector; + +template +class VmaSmallVector; + +template +class VmaPoolAllocator; + +template +struct VmaListItem; + +template +class VmaRawList; + +template +class VmaList; + +template +class VmaIntrusiveLinkedList; + +// Unused in this version +#if 0 +template +struct VmaPair; +template +struct VmaPairFirstLess; + +template +class VmaMap; +#endif + +#if VMA_STATS_STRING_ENABLED +class VmaStringBuilder; +class VmaJsonWriter; +#endif + +class VmaDeviceMemoryBlock; + +struct VmaDedicatedAllocationListItemTraits; +class VmaDedicatedAllocationList; + +struct VmaSuballocation; +struct VmaSuballocationOffsetLess; +struct VmaSuballocationOffsetGreater; +struct VmaSuballocationItemSizeLess; + +typedef VmaList> VmaSuballocationList; + +struct VmaAllocationRequest; + +class VmaBlockMetadata; +class VmaBlockMetadata_Linear; +class VmaBlockMetadata_TLSF; + +class VmaBlockVector; + +struct VmaPoolListItemTraits; + +struct VmaCurrentBudgetData; + +class VmaAllocationObjectAllocator; + +#endif // _VMA_FORWARD_DECLARATIONS + + +#ifndef _VMA_FUNCTIONS + +/* +Returns number of bits set to 1 in (v). + +On specific platforms and compilers you can use instrinsics like: + +Visual Studio: + return __popcnt(v); +GCC, Clang: + return static_cast(__builtin_popcount(v)); + +Define macro VMA_COUNT_BITS_SET to provide your optimized implementation. +But you need to check in runtime whether user's CPU supports these, as some old processors don't. +*/ +static inline uint32_t VmaCountBitsSet(uint32_t v) +{ +#if __cplusplus >= 202002L || _MSVC_LANG >= 202002L // C++20 + return std::popcount(v); +#else + uint32_t c = v - ((v >> 1) & 0x55555555); + c = ((c >> 2) & 0x33333333) + (c & 0x33333333); + c = ((c >> 4) + c) & 0x0F0F0F0F; + c = ((c >> 8) + c) & 0x00FF00FF; + c = ((c >> 16) + c) & 0x0000FFFF; + return c; +#endif +} + +static inline uint8_t VmaBitScanLSB(uint64_t mask) +{ +#if defined(_MSC_VER) && defined(_WIN64) + unsigned long pos; + if (_BitScanForward64(&pos, mask)) + return static_cast(pos); + return UINT8_MAX; +#elif defined __GNUC__ || defined __clang__ + return static_cast(__builtin_ffsll(mask)) - 1U; +#else + uint8_t pos = 0; + uint64_t bit = 1; + do + { + if (mask & bit) + return pos; + bit <<= 1; + } while (pos++ < 63); + return UINT8_MAX; +#endif +} + +static inline uint8_t VmaBitScanLSB(uint32_t mask) +{ +#ifdef _MSC_VER + unsigned long pos; + if (_BitScanForward(&pos, mask)) + return static_cast(pos); + return UINT8_MAX; +#elif defined __GNUC__ || defined __clang__ + return static_cast(__builtin_ffs(mask)) - 1U; +#else + uint8_t pos = 0; + uint32_t bit = 1; + do + { + if (mask & bit) + return pos; + bit <<= 1; + } while (pos++ < 31); + return UINT8_MAX; +#endif +} + +static inline uint8_t VmaBitScanMSB(uint64_t mask) +{ +#if defined(_MSC_VER) && defined(_WIN64) + unsigned long pos; + if (_BitScanReverse64(&pos, mask)) + return static_cast(pos); +#elif defined __GNUC__ || defined __clang__ + if (mask) + return 63 - static_cast(__builtin_clzll(mask)); +#else + uint8_t pos = 63; + uint64_t bit = 1ULL << 63; + do + { + if (mask & bit) + return pos; + bit >>= 1; + } while (pos-- > 0); +#endif + return UINT8_MAX; +} + +static inline uint8_t VmaBitScanMSB(uint32_t mask) +{ +#ifdef _MSC_VER + unsigned long pos; + if (_BitScanReverse(&pos, mask)) + return static_cast(pos); +#elif defined __GNUC__ || defined __clang__ + if (mask) + return 31 - static_cast(__builtin_clz(mask)); +#else + uint8_t pos = 31; + uint32_t bit = 1UL << 31; + do + { + if (mask & bit) + return pos; + bit >>= 1; + } while (pos-- > 0); +#endif + return UINT8_MAX; +} + +/* +Returns true if given number is a power of two. +T must be unsigned integer number or signed integer but always nonnegative. +For 0 returns true. +*/ +template +inline bool VmaIsPow2(T x) +{ + return (x & (x - 1)) == 0; +} + +// Aligns given value up to nearest multiply of align value. For example: VmaAlignUp(11, 8) = 16. +// Use types like uint32_t, uint64_t as T. +template +static inline T VmaAlignUp(T val, T alignment) +{ + VMA_HEAVY_ASSERT(VmaIsPow2(alignment)); + return (val + alignment - 1) & ~(alignment - 1); +} + +// Aligns given value down to nearest multiply of align value. For example: VmaAlignDown(11, 8) = 8. +// Use types like uint32_t, uint64_t as T. +template +static inline T VmaAlignDown(T val, T alignment) +{ + VMA_HEAVY_ASSERT(VmaIsPow2(alignment)); + return val & ~(alignment - 1); +} + +// Division with mathematical rounding to nearest number. +template +static inline T VmaRoundDiv(T x, T y) +{ + return (x + (y / (T)2)) / y; +} + +// Divide by 'y' and round up to nearest integer. +template +static inline T VmaDivideRoundingUp(T x, T y) +{ + return (x + y - (T)1) / y; +} + +// Returns smallest power of 2 greater or equal to v. +static inline uint32_t VmaNextPow2(uint32_t v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v++; + return v; +} + +static inline uint64_t VmaNextPow2(uint64_t v) +{ + v--; + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + v++; + return v; +} + +// Returns largest power of 2 less or equal to v. +static inline uint32_t VmaPrevPow2(uint32_t v) +{ + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v = v ^ (v >> 1); + return v; +} + +static inline uint64_t VmaPrevPow2(uint64_t v) +{ + v |= v >> 1; + v |= v >> 2; + v |= v >> 4; + v |= v >> 8; + v |= v >> 16; + v |= v >> 32; + v = v ^ (v >> 1); + return v; +} + +static inline bool VmaStrIsEmpty(const char* pStr) +{ + return pStr == VMA_NULL || *pStr == '\0'; +} + +/* +Returns true if two memory blocks occupy overlapping pages. +ResourceA must be in less memory offset than ResourceB. + +Algorithm is based on "Vulkan 1.0.39 - A Specification (with all registered Vulkan extensions)" +chapter 11.6 "Resource Memory Association", paragraph "Buffer-Image Granularity". +*/ +static inline bool VmaBlocksOnSamePage( + VkDeviceSize resourceAOffset, + VkDeviceSize resourceASize, + VkDeviceSize resourceBOffset, + VkDeviceSize pageSize) +{ + VMA_ASSERT(resourceAOffset + resourceASize <= resourceBOffset && resourceASize > 0 && pageSize > 0); + VkDeviceSize resourceAEnd = resourceAOffset + resourceASize - 1; + VkDeviceSize resourceAEndPage = resourceAEnd & ~(pageSize - 1); + VkDeviceSize resourceBStart = resourceBOffset; + VkDeviceSize resourceBStartPage = resourceBStart & ~(pageSize - 1); + return resourceAEndPage == resourceBStartPage; +} + +/* +Returns true if given suballocation types could conflict and must respect +VkPhysicalDeviceLimits::bufferImageGranularity. They conflict if one is buffer +or linear image and another one is optimal image. If type is unknown, behave +conservatively. +*/ +static inline bool VmaIsBufferImageGranularityConflict( + VmaSuballocationType suballocType1, + VmaSuballocationType suballocType2) +{ + if (suballocType1 > suballocType2) + { + VMA_SWAP(suballocType1, suballocType2); + } + + switch (suballocType1) + { + case VMA_SUBALLOCATION_TYPE_FREE: + return false; + case VMA_SUBALLOCATION_TYPE_UNKNOWN: + return true; + case VMA_SUBALLOCATION_TYPE_BUFFER: + return + suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN || + suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL; + case VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN: + return + suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN || + suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR || + suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL; + case VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR: + return + suballocType2 == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL; + case VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL: + return false; + default: + VMA_ASSERT(0); + return true; + } +} + +static void VmaWriteMagicValue(void* pData, VkDeviceSize offset) +{ +#if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION + uint32_t* pDst = (uint32_t*)((char*)pData + offset); + const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t); + for (size_t i = 0; i < numberCount; ++i, ++pDst) + { + *pDst = VMA_CORRUPTION_DETECTION_MAGIC_VALUE; + } +#else + // no-op +#endif +} + +static bool VmaValidateMagicValue(const void* pData, VkDeviceSize offset) +{ +#if VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_DETECT_CORRUPTION + const uint32_t* pSrc = (const uint32_t*)((const char*)pData + offset); + const size_t numberCount = VMA_DEBUG_MARGIN / sizeof(uint32_t); + for (size_t i = 0; i < numberCount; ++i, ++pSrc) + { + if (*pSrc != VMA_CORRUPTION_DETECTION_MAGIC_VALUE) + { + return false; + } + } +#endif + return true; +} + +/* +Fills structure with parameters of an example buffer to be used for transfers +during GPU memory defragmentation. +*/ +static void VmaFillGpuDefragmentationBufferCreateInfo(VkBufferCreateInfo& outBufCreateInfo) +{ + memset(&outBufCreateInfo, 0, sizeof(outBufCreateInfo)); + outBufCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + outBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + outBufCreateInfo.size = (VkDeviceSize)VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE; // Example size. +} + + +/* +Performs binary search and returns iterator to first element that is greater or +equal to (key), according to comparison (cmp). + +Cmp should return true if first argument is less than second argument. + +Returned value is the found element, if present in the collection or place where +new element with value (key) should be inserted. +*/ +template +static IterT VmaBinaryFindFirstNotLess(IterT beg, IterT end, const KeyT& key, const CmpLess& cmp) +{ + size_t down = 0, up = (end - beg); + while (down < up) + { + const size_t mid = down + (up - down) / 2; // Overflow-safe midpoint calculation + if (cmp(*(beg + mid), key)) + { + down = mid + 1; + } + else + { + up = mid; + } + } + return beg + down; +} + +template +IterT VmaBinaryFindSorted(const IterT& beg, const IterT& end, const KeyT& value, const CmpLess& cmp) +{ + IterT it = VmaBinaryFindFirstNotLess( + beg, end, value, cmp); + if (it == end || + (!cmp(*it, value) && !cmp(value, *it))) + { + return it; + } + return end; +} + +/* +Returns true if all pointers in the array are not-null and unique. +Warning! O(n^2) complexity. Use only inside VMA_HEAVY_ASSERT. +T must be pointer type, e.g. VmaAllocation, VmaPool. +*/ +template +static bool VmaValidatePointerArray(uint32_t count, const T* arr) +{ + for (uint32_t i = 0; i < count; ++i) + { + const T iPtr = arr[i]; + if (iPtr == VMA_NULL) + { + return false; + } + for (uint32_t j = i + 1; j < count; ++j) + { + if (iPtr == arr[j]) + { + return false; + } + } + } + return true; +} + +template +static inline void VmaPnextChainPushFront(MainT* mainStruct, NewT* newStruct) +{ + newStruct->pNext = mainStruct->pNext; + mainStruct->pNext = newStruct; +} + +// This is the main algorithm that guides the selection of a memory type best for an allocation - +// converts usage to required/preferred/not preferred flags. +static bool FindMemoryPreferences( + bool isIntegratedGPU, + const VmaAllocationCreateInfo& allocCreateInfo, + VkFlags bufImgUsage, // VkBufferCreateInfo::usage or VkImageCreateInfo::usage. UINT32_MAX if unknown. + VkMemoryPropertyFlags& outRequiredFlags, + VkMemoryPropertyFlags& outPreferredFlags, + VkMemoryPropertyFlags& outNotPreferredFlags) +{ + outRequiredFlags = allocCreateInfo.requiredFlags; + outPreferredFlags = allocCreateInfo.preferredFlags; + outNotPreferredFlags = 0; + + switch(allocCreateInfo.usage) + { + case VMA_MEMORY_USAGE_UNKNOWN: + break; + case VMA_MEMORY_USAGE_GPU_ONLY: + if(!isIntegratedGPU || (outPreferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) + { + outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + } + break; + case VMA_MEMORY_USAGE_CPU_ONLY: + outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + break; + case VMA_MEMORY_USAGE_CPU_TO_GPU: + outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + if(!isIntegratedGPU || (outPreferredFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) + { + outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + } + break; + case VMA_MEMORY_USAGE_GPU_TO_CPU: + outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + outPreferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT; + break; + case VMA_MEMORY_USAGE_CPU_COPY: + outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + break; + case VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED: + outRequiredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT; + break; + case VMA_MEMORY_USAGE_AUTO: + case VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE: + case VMA_MEMORY_USAGE_AUTO_PREFER_HOST: + { + if(bufImgUsage == UINT32_MAX) + { + VMA_ASSERT(0 && "VMA_MEMORY_USAGE_AUTO* values can only be used with functions like vmaCreateBuffer, vmaCreateImage so that the details of the created resource are known."); + return false; + } + // This relies on values of VK_IMAGE_USAGE_TRANSFER* being the same VK_BUFFER_IMAGE_TRANSFER*. + const bool deviceAccess = (bufImgUsage & ~(VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT)) != 0; + const bool hostAccessSequentialWrite = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT) != 0; + const bool hostAccessRandom = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT) != 0; + const bool hostAccessAllowTransferInstead = (allocCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT) != 0; + const bool preferDevice = allocCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; + const bool preferHost = allocCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_HOST; + + // CPU random access - e.g. a buffer written to or transferred from GPU to read back on CPU. + if(hostAccessRandom) + { + if(!isIntegratedGPU && deviceAccess && hostAccessAllowTransferInstead && !preferHost) + { + // Nice if it will end up in HOST_VISIBLE, but more importantly prefer DEVICE_LOCAL. + // Omitting HOST_VISIBLE here is intentional. + // In case there is DEVICE_LOCAL | HOST_VISIBLE | HOST_CACHED, it will pick that one. + // Otherwise, this will give same weight to DEVICE_LOCAL as HOST_VISIBLE | HOST_CACHED and select the former if occurs first on the list. + outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; + } + else + { + // Always CPU memory, cached. + outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; + } + } + // CPU sequential write - may be CPU or host-visible GPU memory, uncached and write-combined. + else if(hostAccessSequentialWrite) + { + // Want uncached and write-combined. + outNotPreferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT; + + if(!isIntegratedGPU && deviceAccess && hostAccessAllowTransferInstead && !preferHost) + { + outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + } + else + { + outRequiredFlags |= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + // Direct GPU access, CPU sequential write (e.g. a dynamic uniform buffer updated every frame) + if(deviceAccess) + { + // Could go to CPU memory or GPU BAR/unified. Up to the user to decide. If no preference, choose GPU memory. + if(preferHost) + outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + else + outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + } + // GPU no direct access, CPU sequential write (e.g. an upload buffer to be transferred to the GPU) + else + { + // Could go to CPU memory or GPU BAR/unified. Up to the user to decide. If no preference, choose CPU memory. + if(preferDevice) + outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + else + outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + } + } + } + // No CPU access + else + { + // GPU access, no CPU access (e.g. a color attachment image) - prefer GPU memory + if(deviceAccess) + { + // ...unless there is a clear preference from the user not to do so. + if(preferHost) + outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + else + outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + } + // No direct GPU access, no CPU access, just transfers. + // It may be staging copy intended for e.g. preserving image for next frame (then better GPU memory) or + // a "swap file" copy to free some GPU memory (then better CPU memory). + // Up to the user to decide. If no preferece, assume the former and choose GPU memory. + if(preferHost) + outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + else + outPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + } + break; + } + default: + VMA_ASSERT(0); + } + + // Avoid DEVICE_COHERENT unless explicitly requested. + if(((allocCreateInfo.requiredFlags | allocCreateInfo.preferredFlags) & + (VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY)) == 0) + { + outNotPreferredFlags |= VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY; + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// Memory allocation + +static void* VmaMalloc(const VkAllocationCallbacks* pAllocationCallbacks, size_t size, size_t alignment) +{ + void* result = VMA_NULL; + if ((pAllocationCallbacks != VMA_NULL) && + (pAllocationCallbacks->pfnAllocation != VMA_NULL)) + { + result = (*pAllocationCallbacks->pfnAllocation)( + pAllocationCallbacks->pUserData, + size, + alignment, + VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); + } + else + { + result = VMA_SYSTEM_ALIGNED_MALLOC(size, alignment); + } + VMA_ASSERT(result != VMA_NULL && "CPU memory allocation failed."); + return result; +} + +static void VmaFree(const VkAllocationCallbacks* pAllocationCallbacks, void* ptr) +{ + if ((pAllocationCallbacks != VMA_NULL) && + (pAllocationCallbacks->pfnFree != VMA_NULL)) + { + (*pAllocationCallbacks->pfnFree)(pAllocationCallbacks->pUserData, ptr); + } + else + { + VMA_SYSTEM_ALIGNED_FREE(ptr); + } +} + +template +static T* VmaAllocate(const VkAllocationCallbacks* pAllocationCallbacks) +{ + return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T), VMA_ALIGN_OF(T)); +} + +template +static T* VmaAllocateArray(const VkAllocationCallbacks* pAllocationCallbacks, size_t count) +{ + return (T*)VmaMalloc(pAllocationCallbacks, sizeof(T) * count, VMA_ALIGN_OF(T)); +} + +#define vma_new(allocator, type) new(VmaAllocate(allocator))(type) + +#define vma_new_array(allocator, type, count) new(VmaAllocateArray((allocator), (count)))(type) + +template +static void vma_delete(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr) +{ + ptr->~T(); + VmaFree(pAllocationCallbacks, ptr); +} + +template +static void vma_delete_array(const VkAllocationCallbacks* pAllocationCallbacks, T* ptr, size_t count) +{ + if (ptr != VMA_NULL) + { + for (size_t i = count; i--; ) + { + ptr[i].~T(); + } + VmaFree(pAllocationCallbacks, ptr); + } +} + +static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr) +{ + if (srcStr != VMA_NULL) + { + const size_t len = strlen(srcStr); + char* const result = vma_new_array(allocs, char, len + 1); + memcpy(result, srcStr, len + 1); + return result; + } + return VMA_NULL; +} + +#if VMA_STATS_STRING_ENABLED +static char* VmaCreateStringCopy(const VkAllocationCallbacks* allocs, const char* srcStr, size_t strLen) +{ + if (srcStr != VMA_NULL) + { + char* const result = vma_new_array(allocs, char, strLen + 1); + memcpy(result, srcStr, strLen); + result[strLen] = '\0'; + return result; + } + return VMA_NULL; +} +#endif // VMA_STATS_STRING_ENABLED + +static void VmaFreeString(const VkAllocationCallbacks* allocs, char* str) +{ + if (str != VMA_NULL) + { + const size_t len = strlen(str); + vma_delete_array(allocs, str, len + 1); + } +} + +template +size_t VmaVectorInsertSorted(VectorT& vector, const typename VectorT::value_type& value) +{ + const size_t indexToInsert = VmaBinaryFindFirstNotLess( + vector.data(), + vector.data() + vector.size(), + value, + CmpLess()) - vector.data(); + VmaVectorInsert(vector, indexToInsert, value); + return indexToInsert; +} + +template +bool VmaVectorRemoveSorted(VectorT& vector, const typename VectorT::value_type& value) +{ + CmpLess comparator; + typename VectorT::iterator it = VmaBinaryFindFirstNotLess( + vector.begin(), + vector.end(), + value, + comparator); + if ((it != vector.end()) && !comparator(*it, value) && !comparator(value, *it)) + { + size_t indexToRemove = it - vector.begin(); + VmaVectorRemove(vector, indexToRemove); + return true; + } + return false; +} +#endif // _VMA_FUNCTIONS + +#ifndef _VMA_STATISTICS_FUNCTIONS + +static void VmaClearStatistics(VmaStatistics& outStats) +{ + outStats.blockCount = 0; + outStats.allocationCount = 0; + outStats.blockBytes = 0; + outStats.allocationBytes = 0; +} + +static void VmaAddStatistics(VmaStatistics& inoutStats, const VmaStatistics& src) +{ + inoutStats.blockCount += src.blockCount; + inoutStats.allocationCount += src.allocationCount; + inoutStats.blockBytes += src.blockBytes; + inoutStats.allocationBytes += src.allocationBytes; +} + +static void VmaClearDetailedStatistics(VmaDetailedStatistics& outStats) +{ + VmaClearStatistics(outStats.statistics); + outStats.unusedRangeCount = 0; + outStats.allocationSizeMin = VK_WHOLE_SIZE; + outStats.allocationSizeMax = 0; + outStats.unusedRangeSizeMin = VK_WHOLE_SIZE; + outStats.unusedRangeSizeMax = 0; +} + +static void VmaAddDetailedStatisticsAllocation(VmaDetailedStatistics& inoutStats, VkDeviceSize size) +{ + inoutStats.statistics.allocationCount++; + inoutStats.statistics.allocationBytes += size; + inoutStats.allocationSizeMin = VMA_MIN(inoutStats.allocationSizeMin, size); + inoutStats.allocationSizeMax = VMA_MAX(inoutStats.allocationSizeMax, size); +} + +static void VmaAddDetailedStatisticsUnusedRange(VmaDetailedStatistics& inoutStats, VkDeviceSize size) +{ + inoutStats.unusedRangeCount++; + inoutStats.unusedRangeSizeMin = VMA_MIN(inoutStats.unusedRangeSizeMin, size); + inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, size); +} + +static void VmaAddDetailedStatistics(VmaDetailedStatistics& inoutStats, const VmaDetailedStatistics& src) +{ + VmaAddStatistics(inoutStats.statistics, src.statistics); + inoutStats.unusedRangeCount += src.unusedRangeCount; + inoutStats.allocationSizeMin = VMA_MIN(inoutStats.allocationSizeMin, src.allocationSizeMin); + inoutStats.allocationSizeMax = VMA_MAX(inoutStats.allocationSizeMax, src.allocationSizeMax); + inoutStats.unusedRangeSizeMin = VMA_MIN(inoutStats.unusedRangeSizeMin, src.unusedRangeSizeMin); + inoutStats.unusedRangeSizeMax = VMA_MAX(inoutStats.unusedRangeSizeMax, src.unusedRangeSizeMax); +} + +#endif // _VMA_STATISTICS_FUNCTIONS + +#ifndef _VMA_MUTEX_LOCK +// Helper RAII class to lock a mutex in constructor and unlock it in destructor (at the end of scope). +struct VmaMutexLock +{ + VMA_CLASS_NO_COPY(VmaMutexLock) +public: + VmaMutexLock(VMA_MUTEX& mutex, bool useMutex = true) : + m_pMutex(useMutex ? &mutex : VMA_NULL) + { + if (m_pMutex) { m_pMutex->Lock(); } + } + ~VmaMutexLock() { if (m_pMutex) { m_pMutex->Unlock(); } } + +private: + VMA_MUTEX* m_pMutex; +}; + +// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for reading. +struct VmaMutexLockRead +{ + VMA_CLASS_NO_COPY(VmaMutexLockRead) +public: + VmaMutexLockRead(VMA_RW_MUTEX& mutex, bool useMutex) : + m_pMutex(useMutex ? &mutex : VMA_NULL) + { + if (m_pMutex) { m_pMutex->LockRead(); } + } + ~VmaMutexLockRead() { if (m_pMutex) { m_pMutex->UnlockRead(); } } + +private: + VMA_RW_MUTEX* m_pMutex; +}; + +// Helper RAII class to lock a RW mutex in constructor and unlock it in destructor (at the end of scope), for writing. +struct VmaMutexLockWrite +{ + VMA_CLASS_NO_COPY(VmaMutexLockWrite) +public: + VmaMutexLockWrite(VMA_RW_MUTEX& mutex, bool useMutex) + : m_pMutex(useMutex ? &mutex : VMA_NULL) + { + if (m_pMutex) { m_pMutex->LockWrite(); } + } + ~VmaMutexLockWrite() { if (m_pMutex) { m_pMutex->UnlockWrite(); } } + +private: + VMA_RW_MUTEX* m_pMutex; +}; + +#if VMA_DEBUG_GLOBAL_MUTEX + static VMA_MUTEX gDebugGlobalMutex; + #define VMA_DEBUG_GLOBAL_MUTEX_LOCK VmaMutexLock debugGlobalMutexLock(gDebugGlobalMutex, true); +#else + #define VMA_DEBUG_GLOBAL_MUTEX_LOCK +#endif +#endif // _VMA_MUTEX_LOCK + +#ifndef _VMA_ATOMIC_TRANSACTIONAL_INCREMENT +// An object that increments given atomic but decrements it back in the destructor unless Commit() is called. +template +struct AtomicTransactionalIncrement +{ +public: + using T = decltype(AtomicT().load()); + + ~AtomicTransactionalIncrement() + { + if(m_Atomic) + --(*m_Atomic); + } + + void Commit() { m_Atomic = nullptr; } + T Increment(AtomicT* atomic) + { + m_Atomic = atomic; + return m_Atomic->fetch_add(1); + } + +private: + AtomicT* m_Atomic = nullptr; +}; +#endif // _VMA_ATOMIC_TRANSACTIONAL_INCREMENT + +#ifndef _VMA_STL_ALLOCATOR +// STL-compatible allocator. +template +struct VmaStlAllocator +{ + const VkAllocationCallbacks* const m_pCallbacks; + typedef T value_type; + + VmaStlAllocator(const VkAllocationCallbacks* pCallbacks) : m_pCallbacks(pCallbacks) {} + template + VmaStlAllocator(const VmaStlAllocator& src) : m_pCallbacks(src.m_pCallbacks) {} + VmaStlAllocator(const VmaStlAllocator&) = default; + VmaStlAllocator& operator=(const VmaStlAllocator&) = delete; + + T* allocate(size_t n) { return VmaAllocateArray(m_pCallbacks, n); } + void deallocate(T* p, size_t n) { VmaFree(m_pCallbacks, p); } + + template + bool operator==(const VmaStlAllocator& rhs) const + { + return m_pCallbacks == rhs.m_pCallbacks; + } + template + bool operator!=(const VmaStlAllocator& rhs) const + { + return m_pCallbacks != rhs.m_pCallbacks; + } +}; +#endif // _VMA_STL_ALLOCATOR + +#ifndef _VMA_VECTOR +/* Class with interface compatible with subset of std::vector. +T must be POD because constructors and destructors are not called and memcpy is +used for these objects. */ +template +class VmaVector +{ +public: + typedef T value_type; + typedef T* iterator; + typedef const T* const_iterator; + + VmaVector(const AllocatorT& allocator); + VmaVector(size_t count, const AllocatorT& allocator); + // This version of the constructor is here for compatibility with pre-C++14 std::vector. + // value is unused. + VmaVector(size_t count, const T& value, const AllocatorT& allocator) : VmaVector(count, allocator) {} + VmaVector(const VmaVector& src); + VmaVector& operator=(const VmaVector& rhs); + ~VmaVector() { VmaFree(m_Allocator.m_pCallbacks, m_pArray); } + + bool empty() const { return m_Count == 0; } + size_t size() const { return m_Count; } + T* data() { return m_pArray; } + T& front() { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[0]; } + T& back() { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[m_Count - 1]; } + const T* data() const { return m_pArray; } + const T& front() const { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[0]; } + const T& back() const { VMA_HEAVY_ASSERT(m_Count > 0); return m_pArray[m_Count - 1]; } + + iterator begin() { return m_pArray; } + iterator end() { return m_pArray + m_Count; } + const_iterator cbegin() const { return m_pArray; } + const_iterator cend() const { return m_pArray + m_Count; } + const_iterator begin() const { return cbegin(); } + const_iterator end() const { return cend(); } + + void pop_front() { VMA_HEAVY_ASSERT(m_Count > 0); remove(0); } + void pop_back() { VMA_HEAVY_ASSERT(m_Count > 0); resize(size() - 1); } + void push_front(const T& src) { insert(0, src); } + + void push_back(const T& src); + void reserve(size_t newCapacity, bool freeMemory = false); + void resize(size_t newCount); + void clear() { resize(0); } + void shrink_to_fit(); + void insert(size_t index, const T& src); + void remove(size_t index); + + T& operator[](size_t index) { VMA_HEAVY_ASSERT(index < m_Count); return m_pArray[index]; } + const T& operator[](size_t index) const { VMA_HEAVY_ASSERT(index < m_Count); return m_pArray[index]; } + +private: + AllocatorT m_Allocator; + T* m_pArray; + size_t m_Count; + size_t m_Capacity; +}; + +#ifndef _VMA_VECTOR_FUNCTIONS +template +VmaVector::VmaVector(const AllocatorT& allocator) + : m_Allocator(allocator), + m_pArray(VMA_NULL), + m_Count(0), + m_Capacity(0) {} + +template +VmaVector::VmaVector(size_t count, const AllocatorT& allocator) + : m_Allocator(allocator), + m_pArray(count ? (T*)VmaAllocateArray(allocator.m_pCallbacks, count) : VMA_NULL), + m_Count(count), + m_Capacity(count) {} + +template +VmaVector::VmaVector(const VmaVector& src) + : m_Allocator(src.m_Allocator), + m_pArray(src.m_Count ? (T*)VmaAllocateArray(src.m_Allocator.m_pCallbacks, src.m_Count) : VMA_NULL), + m_Count(src.m_Count), + m_Capacity(src.m_Count) +{ + if (m_Count != 0) + { + memcpy(m_pArray, src.m_pArray, m_Count * sizeof(T)); + } +} + +template +VmaVector& VmaVector::operator=(const VmaVector& rhs) +{ + if (&rhs != this) + { + resize(rhs.m_Count); + if (m_Count != 0) + { + memcpy(m_pArray, rhs.m_pArray, m_Count * sizeof(T)); + } + } + return *this; +} + +template +void VmaVector::push_back(const T& src) +{ + const size_t newIndex = size(); + resize(newIndex + 1); + m_pArray[newIndex] = src; +} + +template +void VmaVector::reserve(size_t newCapacity, bool freeMemory) +{ + newCapacity = VMA_MAX(newCapacity, m_Count); + + if ((newCapacity < m_Capacity) && !freeMemory) + { + newCapacity = m_Capacity; + } + + if (newCapacity != m_Capacity) + { + T* const newArray = newCapacity ? VmaAllocateArray(m_Allocator, newCapacity) : VMA_NULL; + if (m_Count != 0) + { + memcpy(newArray, m_pArray, m_Count * sizeof(T)); + } + VmaFree(m_Allocator.m_pCallbacks, m_pArray); + m_Capacity = newCapacity; + m_pArray = newArray; + } +} + +template +void VmaVector::resize(size_t newCount) +{ + size_t newCapacity = m_Capacity; + if (newCount > m_Capacity) + { + newCapacity = VMA_MAX(newCount, VMA_MAX(m_Capacity * 3 / 2, (size_t)8)); + } + + if (newCapacity != m_Capacity) + { + T* const newArray = newCapacity ? VmaAllocateArray(m_Allocator.m_pCallbacks, newCapacity) : VMA_NULL; + const size_t elementsToCopy = VMA_MIN(m_Count, newCount); + if (elementsToCopy != 0) + { + memcpy(newArray, m_pArray, elementsToCopy * sizeof(T)); + } + VmaFree(m_Allocator.m_pCallbacks, m_pArray); + m_Capacity = newCapacity; + m_pArray = newArray; + } + + m_Count = newCount; +} + +template +void VmaVector::shrink_to_fit() +{ + if (m_Capacity > m_Count) + { + T* newArray = VMA_NULL; + if (m_Count > 0) + { + newArray = VmaAllocateArray(m_Allocator.m_pCallbacks, m_Count); + memcpy(newArray, m_pArray, m_Count * sizeof(T)); + } + VmaFree(m_Allocator.m_pCallbacks, m_pArray); + m_Capacity = m_Count; + m_pArray = newArray; + } +} + +template +void VmaVector::insert(size_t index, const T& src) +{ + VMA_HEAVY_ASSERT(index <= m_Count); + const size_t oldCount = size(); + resize(oldCount + 1); + if (index < oldCount) + { + memmove(m_pArray + (index + 1), m_pArray + index, (oldCount - index) * sizeof(T)); + } + m_pArray[index] = src; +} + +template +void VmaVector::remove(size_t index) +{ + VMA_HEAVY_ASSERT(index < m_Count); + const size_t oldCount = size(); + if (index < oldCount - 1) + { + memmove(m_pArray + index, m_pArray + (index + 1), (oldCount - index - 1) * sizeof(T)); + } + resize(oldCount - 1); +} +#endif // _VMA_VECTOR_FUNCTIONS + +template +static void VmaVectorInsert(VmaVector& vec, size_t index, const T& item) +{ + vec.insert(index, item); +} + +template +static void VmaVectorRemove(VmaVector& vec, size_t index) +{ + vec.remove(index); +} +#endif // _VMA_VECTOR + +#ifndef _VMA_SMALL_VECTOR +/* +This is a vector (a variable-sized array), optimized for the case when the array is small. + +It contains some number of elements in-place, which allows it to avoid heap allocation +when the actual number of elements is below that threshold. This allows normal "small" +cases to be fast without losing generality for large inputs. +*/ +template +class VmaSmallVector +{ +public: + typedef T value_type; + typedef T* iterator; + + VmaSmallVector(const AllocatorT& allocator); + VmaSmallVector(size_t count, const AllocatorT& allocator); + template + VmaSmallVector(const VmaSmallVector&) = delete; + template + VmaSmallVector& operator=(const VmaSmallVector&) = delete; + ~VmaSmallVector() = default; + + bool empty() const { return m_Count == 0; } + size_t size() const { return m_Count; } + T* data() { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; } + T& front() { VMA_HEAVY_ASSERT(m_Count > 0); return data()[0]; } + T& back() { VMA_HEAVY_ASSERT(m_Count > 0); return data()[m_Count - 1]; } + const T* data() const { return m_Count > N ? m_DynamicArray.data() : m_StaticArray; } + const T& front() const { VMA_HEAVY_ASSERT(m_Count > 0); return data()[0]; } + const T& back() const { VMA_HEAVY_ASSERT(m_Count > 0); return data()[m_Count - 1]; } + + iterator begin() { return data(); } + iterator end() { return data() + m_Count; } + + void pop_front() { VMA_HEAVY_ASSERT(m_Count > 0); remove(0); } + void pop_back() { VMA_HEAVY_ASSERT(m_Count > 0); resize(size() - 1); } + void push_front(const T& src) { insert(0, src); } + + void push_back(const T& src); + void resize(size_t newCount, bool freeMemory = false); + void clear(bool freeMemory = false); + void insert(size_t index, const T& src); + void remove(size_t index); + + T& operator[](size_t index) { VMA_HEAVY_ASSERT(index < m_Count); return data()[index]; } + const T& operator[](size_t index) const { VMA_HEAVY_ASSERT(index < m_Count); return data()[index]; } + +private: + size_t m_Count; + T m_StaticArray[N]; // Used when m_Size <= N + VmaVector m_DynamicArray; // Used when m_Size > N +}; + +#ifndef _VMA_SMALL_VECTOR_FUNCTIONS +template +VmaSmallVector::VmaSmallVector(const AllocatorT& allocator) + : m_Count(0), + m_DynamicArray(allocator) {} + +template +VmaSmallVector::VmaSmallVector(size_t count, const AllocatorT& allocator) + : m_Count(count), + m_DynamicArray(count > N ? count : 0, allocator) {} + +template +void VmaSmallVector::push_back(const T& src) +{ + const size_t newIndex = size(); + resize(newIndex + 1); + data()[newIndex] = src; +} + +template +void VmaSmallVector::resize(size_t newCount, bool freeMemory) +{ + if (newCount > N && m_Count > N) + { + // Any direction, staying in m_DynamicArray + m_DynamicArray.resize(newCount); + if (freeMemory) + { + m_DynamicArray.shrink_to_fit(); + } + } + else if (newCount > N && m_Count <= N) + { + // Growing, moving from m_StaticArray to m_DynamicArray + m_DynamicArray.resize(newCount); + if (m_Count > 0) + { + memcpy(m_DynamicArray.data(), m_StaticArray, m_Count * sizeof(T)); + } + } + else if (newCount <= N && m_Count > N) + { + // Shrinking, moving from m_DynamicArray to m_StaticArray + if (newCount > 0) + { + memcpy(m_StaticArray, m_DynamicArray.data(), newCount * sizeof(T)); + } + m_DynamicArray.resize(0); + if (freeMemory) + { + m_DynamicArray.shrink_to_fit(); + } + } + else + { + // Any direction, staying in m_StaticArray - nothing to do here + } + m_Count = newCount; +} + +template +void VmaSmallVector::clear(bool freeMemory) +{ + m_DynamicArray.clear(); + if (freeMemory) + { + m_DynamicArray.shrink_to_fit(); + } + m_Count = 0; +} + +template +void VmaSmallVector::insert(size_t index, const T& src) +{ + VMA_HEAVY_ASSERT(index <= m_Count); + const size_t oldCount = size(); + resize(oldCount + 1); + T* const dataPtr = data(); + if (index < oldCount) + { + // I know, this could be more optimal for case where memmove can be memcpy directly from m_StaticArray to m_DynamicArray. + memmove(dataPtr + (index + 1), dataPtr + index, (oldCount - index) * sizeof(T)); + } + dataPtr[index] = src; +} + +template +void VmaSmallVector::remove(size_t index) +{ + VMA_HEAVY_ASSERT(index < m_Count); + const size_t oldCount = size(); + if (index < oldCount - 1) + { + // I know, this could be more optimal for case where memmove can be memcpy directly from m_DynamicArray to m_StaticArray. + T* const dataPtr = data(); + memmove(dataPtr + index, dataPtr + (index + 1), (oldCount - index - 1) * sizeof(T)); + } + resize(oldCount - 1); +} +#endif // _VMA_SMALL_VECTOR_FUNCTIONS +#endif // _VMA_SMALL_VECTOR + +#ifndef _VMA_POOL_ALLOCATOR +/* +Allocator for objects of type T using a list of arrays (pools) to speed up +allocation. Number of elements that can be allocated is not bounded because +allocator can create multiple blocks. +*/ +template +class VmaPoolAllocator +{ + VMA_CLASS_NO_COPY(VmaPoolAllocator) +public: + VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity); + ~VmaPoolAllocator(); + template T* Alloc(Types&&... args); + void Free(T* ptr); + +private: + union Item + { + uint32_t NextFreeIndex; + alignas(T) char Value[sizeof(T)]; + }; + struct ItemBlock + { + Item* pItems; + uint32_t Capacity; + uint32_t FirstFreeIndex; + }; + + const VkAllocationCallbacks* m_pAllocationCallbacks; + const uint32_t m_FirstBlockCapacity; + VmaVector> m_ItemBlocks; + + ItemBlock& CreateNewBlock(); +}; + +#ifndef _VMA_POOL_ALLOCATOR_FUNCTIONS +template +VmaPoolAllocator::VmaPoolAllocator(const VkAllocationCallbacks* pAllocationCallbacks, uint32_t firstBlockCapacity) + : m_pAllocationCallbacks(pAllocationCallbacks), + m_FirstBlockCapacity(firstBlockCapacity), + m_ItemBlocks(VmaStlAllocator(pAllocationCallbacks)) +{ + VMA_ASSERT(m_FirstBlockCapacity > 1); +} + +template +VmaPoolAllocator::~VmaPoolAllocator() +{ + for (size_t i = m_ItemBlocks.size(); i--;) + vma_delete_array(m_pAllocationCallbacks, m_ItemBlocks[i].pItems, m_ItemBlocks[i].Capacity); + m_ItemBlocks.clear(); +} + +template +template T* VmaPoolAllocator::Alloc(Types&&... args) +{ + for (size_t i = m_ItemBlocks.size(); i--; ) + { + ItemBlock& block = m_ItemBlocks[i]; + // This block has some free items: Use first one. + if (block.FirstFreeIndex != UINT32_MAX) + { + Item* const pItem = &block.pItems[block.FirstFreeIndex]; + block.FirstFreeIndex = pItem->NextFreeIndex; + T* result = (T*)&pItem->Value; + new(result)T(std::forward(args)...); // Explicit constructor call. + return result; + } + } + + // No block has free item: Create new one and use it. + ItemBlock& newBlock = CreateNewBlock(); + Item* const pItem = &newBlock.pItems[0]; + newBlock.FirstFreeIndex = pItem->NextFreeIndex; + T* result = (T*)&pItem->Value; + new(result) T(std::forward(args)...); // Explicit constructor call. + return result; +} + +template +void VmaPoolAllocator::Free(T* ptr) +{ + // Search all memory blocks to find ptr. + for (size_t i = m_ItemBlocks.size(); i--; ) + { + ItemBlock& block = m_ItemBlocks[i]; + + // Casting to union. + Item* pItemPtr; + memcpy(&pItemPtr, &ptr, sizeof(pItemPtr)); + + // Check if pItemPtr is in address range of this block. + if ((pItemPtr >= block.pItems) && (pItemPtr < block.pItems + block.Capacity)) + { + ptr->~T(); // Explicit destructor call. + const uint32_t index = static_cast(pItemPtr - block.pItems); + pItemPtr->NextFreeIndex = block.FirstFreeIndex; + block.FirstFreeIndex = index; + return; + } + } + VMA_ASSERT(0 && "Pointer doesn't belong to this memory pool."); +} + +template +typename VmaPoolAllocator::ItemBlock& VmaPoolAllocator::CreateNewBlock() +{ + const uint32_t newBlockCapacity = m_ItemBlocks.empty() ? + m_FirstBlockCapacity : m_ItemBlocks.back().Capacity * 3 / 2; + + const ItemBlock newBlock = + { + vma_new_array(m_pAllocationCallbacks, Item, newBlockCapacity), + newBlockCapacity, + 0 + }; + + m_ItemBlocks.push_back(newBlock); + + // Setup singly-linked list of all free items in this block. + for (uint32_t i = 0; i < newBlockCapacity - 1; ++i) + newBlock.pItems[i].NextFreeIndex = i + 1; + newBlock.pItems[newBlockCapacity - 1].NextFreeIndex = UINT32_MAX; + return m_ItemBlocks.back(); +} +#endif // _VMA_POOL_ALLOCATOR_FUNCTIONS +#endif // _VMA_POOL_ALLOCATOR + +#ifndef _VMA_RAW_LIST +template +struct VmaListItem +{ + VmaListItem* pPrev; + VmaListItem* pNext; + T Value; +}; + +// Doubly linked list. +template +class VmaRawList +{ + VMA_CLASS_NO_COPY(VmaRawList) +public: + typedef VmaListItem ItemType; + + VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks); + // Intentionally not calling Clear, because that would be unnecessary + // computations to return all items to m_ItemAllocator as free. + ~VmaRawList() = default; + + size_t GetCount() const { return m_Count; } + bool IsEmpty() const { return m_Count == 0; } + + ItemType* Front() { return m_pFront; } + ItemType* Back() { return m_pBack; } + const ItemType* Front() const { return m_pFront; } + const ItemType* Back() const { return m_pBack; } + + ItemType* PushFront(); + ItemType* PushBack(); + ItemType* PushFront(const T& value); + ItemType* PushBack(const T& value); + void PopFront(); + void PopBack(); + + // Item can be null - it means PushBack. + ItemType* InsertBefore(ItemType* pItem); + // Item can be null - it means PushFront. + ItemType* InsertAfter(ItemType* pItem); + ItemType* InsertBefore(ItemType* pItem, const T& value); + ItemType* InsertAfter(ItemType* pItem, const T& value); + + void Clear(); + void Remove(ItemType* pItem); + +private: + const VkAllocationCallbacks* const m_pAllocationCallbacks; + VmaPoolAllocator m_ItemAllocator; + ItemType* m_pFront; + ItemType* m_pBack; + size_t m_Count; +}; + +#ifndef _VMA_RAW_LIST_FUNCTIONS +template +VmaRawList::VmaRawList(const VkAllocationCallbacks* pAllocationCallbacks) + : m_pAllocationCallbacks(pAllocationCallbacks), + m_ItemAllocator(pAllocationCallbacks, 128), + m_pFront(VMA_NULL), + m_pBack(VMA_NULL), + m_Count(0) {} + +template +VmaListItem* VmaRawList::PushFront() +{ + ItemType* const pNewItem = m_ItemAllocator.Alloc(); + pNewItem->pPrev = VMA_NULL; + if (IsEmpty()) + { + pNewItem->pNext = VMA_NULL; + m_pFront = pNewItem; + m_pBack = pNewItem; + m_Count = 1; + } + else + { + pNewItem->pNext = m_pFront; + m_pFront->pPrev = pNewItem; + m_pFront = pNewItem; + ++m_Count; + } + return pNewItem; +} + +template +VmaListItem* VmaRawList::PushBack() +{ + ItemType* const pNewItem = m_ItemAllocator.Alloc(); + pNewItem->pNext = VMA_NULL; + if(IsEmpty()) + { + pNewItem->pPrev = VMA_NULL; + m_pFront = pNewItem; + m_pBack = pNewItem; + m_Count = 1; + } + else + { + pNewItem->pPrev = m_pBack; + m_pBack->pNext = pNewItem; + m_pBack = pNewItem; + ++m_Count; + } + return pNewItem; +} + +template +VmaListItem* VmaRawList::PushFront(const T& value) +{ + ItemType* const pNewItem = PushFront(); + pNewItem->Value = value; + return pNewItem; +} + +template +VmaListItem* VmaRawList::PushBack(const T& value) +{ + ItemType* const pNewItem = PushBack(); + pNewItem->Value = value; + return pNewItem; +} + +template +void VmaRawList::PopFront() +{ + VMA_HEAVY_ASSERT(m_Count > 0); + ItemType* const pFrontItem = m_pFront; + ItemType* const pNextItem = pFrontItem->pNext; + if (pNextItem != VMA_NULL) + { + pNextItem->pPrev = VMA_NULL; + } + m_pFront = pNextItem; + m_ItemAllocator.Free(pFrontItem); + --m_Count; +} + +template +void VmaRawList::PopBack() +{ + VMA_HEAVY_ASSERT(m_Count > 0); + ItemType* const pBackItem = m_pBack; + ItemType* const pPrevItem = pBackItem->pPrev; + if(pPrevItem != VMA_NULL) + { + pPrevItem->pNext = VMA_NULL; + } + m_pBack = pPrevItem; + m_ItemAllocator.Free(pBackItem); + --m_Count; +} + +template +void VmaRawList::Clear() +{ + if (IsEmpty() == false) + { + ItemType* pItem = m_pBack; + while (pItem != VMA_NULL) + { + ItemType* const pPrevItem = pItem->pPrev; + m_ItemAllocator.Free(pItem); + pItem = pPrevItem; + } + m_pFront = VMA_NULL; + m_pBack = VMA_NULL; + m_Count = 0; + } +} + +template +void VmaRawList::Remove(ItemType* pItem) +{ + VMA_HEAVY_ASSERT(pItem != VMA_NULL); + VMA_HEAVY_ASSERT(m_Count > 0); + + if(pItem->pPrev != VMA_NULL) + { + pItem->pPrev->pNext = pItem->pNext; + } + else + { + VMA_HEAVY_ASSERT(m_pFront == pItem); + m_pFront = pItem->pNext; + } + + if(pItem->pNext != VMA_NULL) + { + pItem->pNext->pPrev = pItem->pPrev; + } + else + { + VMA_HEAVY_ASSERT(m_pBack == pItem); + m_pBack = pItem->pPrev; + } + + m_ItemAllocator.Free(pItem); + --m_Count; +} + +template +VmaListItem* VmaRawList::InsertBefore(ItemType* pItem) +{ + if(pItem != VMA_NULL) + { + ItemType* const prevItem = pItem->pPrev; + ItemType* const newItem = m_ItemAllocator.Alloc(); + newItem->pPrev = prevItem; + newItem->pNext = pItem; + pItem->pPrev = newItem; + if(prevItem != VMA_NULL) + { + prevItem->pNext = newItem; + } + else + { + VMA_HEAVY_ASSERT(m_pFront == pItem); + m_pFront = newItem; + } + ++m_Count; + return newItem; + } + else + return PushBack(); +} + +template +VmaListItem* VmaRawList::InsertAfter(ItemType* pItem) +{ + if(pItem != VMA_NULL) + { + ItemType* const nextItem = pItem->pNext; + ItemType* const newItem = m_ItemAllocator.Alloc(); + newItem->pNext = nextItem; + newItem->pPrev = pItem; + pItem->pNext = newItem; + if(nextItem != VMA_NULL) + { + nextItem->pPrev = newItem; + } + else + { + VMA_HEAVY_ASSERT(m_pBack == pItem); + m_pBack = newItem; + } + ++m_Count; + return newItem; + } + else + return PushFront(); +} + +template +VmaListItem* VmaRawList::InsertBefore(ItemType* pItem, const T& value) +{ + ItemType* const newItem = InsertBefore(pItem); + newItem->Value = value; + return newItem; +} + +template +VmaListItem* VmaRawList::InsertAfter(ItemType* pItem, const T& value) +{ + ItemType* const newItem = InsertAfter(pItem); + newItem->Value = value; + return newItem; +} +#endif // _VMA_RAW_LIST_FUNCTIONS +#endif // _VMA_RAW_LIST + +#ifndef _VMA_LIST +template +class VmaList +{ + VMA_CLASS_NO_COPY(VmaList) +public: + class reverse_iterator; + class const_iterator; + class const_reverse_iterator; + + class iterator + { + friend class const_iterator; + friend class VmaList; + public: + iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {} + iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + + T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; } + T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; } + + bool operator==(const iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; } + bool operator!=(const iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; } + + iterator operator++(int) { iterator result = *this; ++*this; return result; } + iterator operator--(int) { iterator result = *this; --*this; return result; } + + iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pNext; return *this; } + iterator& operator--(); + + private: + VmaRawList* m_pList; + VmaListItem* m_pItem; + + iterator(VmaRawList* pList, VmaListItem* pItem) : m_pList(pList), m_pItem(pItem) {} + }; + class reverse_iterator + { + friend class const_reverse_iterator; + friend class VmaList; + public: + reverse_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {} + reverse_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + + T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; } + T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; } + + bool operator==(const reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; } + bool operator!=(const reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; } + + reverse_iterator operator++(int) { reverse_iterator result = *this; ++* this; return result; } + reverse_iterator operator--(int) { reverse_iterator result = *this; --* this; return result; } + + reverse_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pPrev; return *this; } + reverse_iterator& operator--(); + + private: + VmaRawList* m_pList; + VmaListItem* m_pItem; + + reverse_iterator(VmaRawList* pList, VmaListItem* pItem) : m_pList(pList), m_pItem(pItem) {} + }; + class const_iterator + { + friend class VmaList; + public: + const_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {} + const_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + const_iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + + iterator drop_const() { return { const_cast*>(m_pList), const_cast*>(m_pItem) }; } + + const T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; } + const T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; } + + bool operator==(const const_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; } + bool operator!=(const const_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; } + + const_iterator operator++(int) { const_iterator result = *this; ++* this; return result; } + const_iterator operator--(int) { const_iterator result = *this; --* this; return result; } + + const_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pNext; return *this; } + const_iterator& operator--(); + + private: + const VmaRawList* m_pList; + const VmaListItem* m_pItem; + + const_iterator(const VmaRawList* pList, const VmaListItem* pItem) : m_pList(pList), m_pItem(pItem) {} + }; + class const_reverse_iterator + { + friend class VmaList; + public: + const_reverse_iterator() : m_pList(VMA_NULL), m_pItem(VMA_NULL) {} + const_reverse_iterator(const reverse_iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + const_reverse_iterator(const iterator& src) : m_pList(src.m_pList), m_pItem(src.m_pItem) {} + + reverse_iterator drop_const() { return { const_cast*>(m_pList), const_cast*>(m_pItem) }; } + + const T& operator*() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return m_pItem->Value; } + const T* operator->() const { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); return &m_pItem->Value; } + + bool operator==(const const_reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem == rhs.m_pItem; } + bool operator!=(const const_reverse_iterator& rhs) const { VMA_HEAVY_ASSERT(m_pList == rhs.m_pList); return m_pItem != rhs.m_pItem; } + + const_reverse_iterator operator++(int) { const_reverse_iterator result = *this; ++* this; return result; } + const_reverse_iterator operator--(int) { const_reverse_iterator result = *this; --* this; return result; } + + const_reverse_iterator& operator++() { VMA_HEAVY_ASSERT(m_pItem != VMA_NULL); m_pItem = m_pItem->pPrev; return *this; } + const_reverse_iterator& operator--(); + + private: + const VmaRawList* m_pList; + const VmaListItem* m_pItem; + + const_reverse_iterator(const VmaRawList* pList, const VmaListItem* pItem) : m_pList(pList), m_pItem(pItem) {} + }; + + VmaList(const AllocatorT& allocator) : m_RawList(allocator.m_pCallbacks) {} + + bool empty() const { return m_RawList.IsEmpty(); } + size_t size() const { return m_RawList.GetCount(); } + + iterator begin() { return iterator(&m_RawList, m_RawList.Front()); } + iterator end() { return iterator(&m_RawList, VMA_NULL); } + + const_iterator cbegin() const { return const_iterator(&m_RawList, m_RawList.Front()); } + const_iterator cend() const { return const_iterator(&m_RawList, VMA_NULL); } + + const_iterator begin() const { return cbegin(); } + const_iterator end() const { return cend(); } + + reverse_iterator rbegin() { return reverse_iterator(&m_RawList, m_RawList.Back()); } + reverse_iterator rend() { return reverse_iterator(&m_RawList, VMA_NULL); } + + const_reverse_iterator crbegin() const { return const_reverse_iterator(&m_RawList, m_RawList.Back()); } + const_reverse_iterator crend() const { return const_reverse_iterator(&m_RawList, VMA_NULL); } + + const_reverse_iterator rbegin() const { return crbegin(); } + const_reverse_iterator rend() const { return crend(); } + + void push_back(const T& value) { m_RawList.PushBack(value); } + iterator insert(iterator it, const T& value) { return iterator(&m_RawList, m_RawList.InsertBefore(it.m_pItem, value)); } + + void clear() { m_RawList.Clear(); } + void erase(iterator it) { m_RawList.Remove(it.m_pItem); } + +private: + VmaRawList m_RawList; +}; + +#ifndef _VMA_LIST_FUNCTIONS +template +typename VmaList::iterator& VmaList::iterator::operator--() +{ + if (m_pItem != VMA_NULL) + { + m_pItem = m_pItem->pPrev; + } + else + { + VMA_HEAVY_ASSERT(!m_pList->IsEmpty()); + m_pItem = m_pList->Back(); + } + return *this; +} + +template +typename VmaList::reverse_iterator& VmaList::reverse_iterator::operator--() +{ + if (m_pItem != VMA_NULL) + { + m_pItem = m_pItem->pNext; + } + else + { + VMA_HEAVY_ASSERT(!m_pList->IsEmpty()); + m_pItem = m_pList->Front(); + } + return *this; +} + +template +typename VmaList::const_iterator& VmaList::const_iterator::operator--() +{ + if (m_pItem != VMA_NULL) + { + m_pItem = m_pItem->pPrev; + } + else + { + VMA_HEAVY_ASSERT(!m_pList->IsEmpty()); + m_pItem = m_pList->Back(); + } + return *this; +} + +template +typename VmaList::const_reverse_iterator& VmaList::const_reverse_iterator::operator--() +{ + if (m_pItem != VMA_NULL) + { + m_pItem = m_pItem->pNext; + } + else + { + VMA_HEAVY_ASSERT(!m_pList->IsEmpty()); + m_pItem = m_pList->Back(); + } + return *this; +} +#endif // _VMA_LIST_FUNCTIONS +#endif // _VMA_LIST + +#ifndef _VMA_INTRUSIVE_LINKED_LIST +/* +Expected interface of ItemTypeTraits: +struct MyItemTypeTraits +{ + typedef MyItem ItemType; + static ItemType* GetPrev(const ItemType* item) { return item->myPrevPtr; } + static ItemType* GetNext(const ItemType* item) { return item->myNextPtr; } + static ItemType*& AccessPrev(ItemType* item) { return item->myPrevPtr; } + static ItemType*& AccessNext(ItemType* item) { return item->myNextPtr; } +}; +*/ +template +class VmaIntrusiveLinkedList +{ +public: + typedef typename ItemTypeTraits::ItemType ItemType; + static ItemType* GetPrev(const ItemType* item) { return ItemTypeTraits::GetPrev(item); } + static ItemType* GetNext(const ItemType* item) { return ItemTypeTraits::GetNext(item); } + + // Movable, not copyable. + VmaIntrusiveLinkedList() = default; + VmaIntrusiveLinkedList(VmaIntrusiveLinkedList && src); + VmaIntrusiveLinkedList(const VmaIntrusiveLinkedList&) = delete; + VmaIntrusiveLinkedList& operator=(VmaIntrusiveLinkedList&& src); + VmaIntrusiveLinkedList& operator=(const VmaIntrusiveLinkedList&) = delete; + ~VmaIntrusiveLinkedList() { VMA_HEAVY_ASSERT(IsEmpty()); } + + size_t GetCount() const { return m_Count; } + bool IsEmpty() const { return m_Count == 0; } + ItemType* Front() { return m_Front; } + ItemType* Back() { return m_Back; } + const ItemType* Front() const { return m_Front; } + const ItemType* Back() const { return m_Back; } + + void PushBack(ItemType* item); + void PushFront(ItemType* item); + ItemType* PopBack(); + ItemType* PopFront(); + + // MyItem can be null - it means PushBack. + void InsertBefore(ItemType* existingItem, ItemType* newItem); + // MyItem can be null - it means PushFront. + void InsertAfter(ItemType* existingItem, ItemType* newItem); + void Remove(ItemType* item); + void RemoveAll(); + +private: + ItemType* m_Front = VMA_NULL; + ItemType* m_Back = VMA_NULL; + size_t m_Count = 0; +}; + +#ifndef _VMA_INTRUSIVE_LINKED_LIST_FUNCTIONS +template +VmaIntrusiveLinkedList::VmaIntrusiveLinkedList(VmaIntrusiveLinkedList&& src) + : m_Front(src.m_Front), m_Back(src.m_Back), m_Count(src.m_Count) +{ + src.m_Front = src.m_Back = VMA_NULL; + src.m_Count = 0; +} + +template +VmaIntrusiveLinkedList& VmaIntrusiveLinkedList::operator=(VmaIntrusiveLinkedList&& src) +{ + if (&src != this) + { + VMA_HEAVY_ASSERT(IsEmpty()); + m_Front = src.m_Front; + m_Back = src.m_Back; + m_Count = src.m_Count; + src.m_Front = src.m_Back = VMA_NULL; + src.m_Count = 0; + } + return *this; +} + +template +void VmaIntrusiveLinkedList::PushBack(ItemType* item) +{ + VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL); + if (IsEmpty()) + { + m_Front = item; + m_Back = item; + m_Count = 1; + } + else + { + ItemTypeTraits::AccessPrev(item) = m_Back; + ItemTypeTraits::AccessNext(m_Back) = item; + m_Back = item; + ++m_Count; + } +} + +template +void VmaIntrusiveLinkedList::PushFront(ItemType* item) +{ + VMA_HEAVY_ASSERT(ItemTypeTraits::GetPrev(item) == VMA_NULL && ItemTypeTraits::GetNext(item) == VMA_NULL); + if (IsEmpty()) + { + m_Front = item; + m_Back = item; + m_Count = 1; + } + else + { + ItemTypeTraits::AccessNext(item) = m_Front; + ItemTypeTraits::AccessPrev(m_Front) = item; + m_Front = item; + ++m_Count; + } +} + +template +typename VmaIntrusiveLinkedList::ItemType* VmaIntrusiveLinkedList::PopBack() +{ + VMA_HEAVY_ASSERT(m_Count > 0); + ItemType* const backItem = m_Back; + ItemType* const prevItem = ItemTypeTraits::GetPrev(backItem); + if (prevItem != VMA_NULL) + { + ItemTypeTraits::AccessNext(prevItem) = VMA_NULL; + } + m_Back = prevItem; + --m_Count; + ItemTypeTraits::AccessPrev(backItem) = VMA_NULL; + ItemTypeTraits::AccessNext(backItem) = VMA_NULL; + return backItem; +} + +template +typename VmaIntrusiveLinkedList::ItemType* VmaIntrusiveLinkedList::PopFront() +{ + VMA_HEAVY_ASSERT(m_Count > 0); + ItemType* const frontItem = m_Front; + ItemType* const nextItem = ItemTypeTraits::GetNext(frontItem); + if (nextItem != VMA_NULL) + { + ItemTypeTraits::AccessPrev(nextItem) = VMA_NULL; + } + m_Front = nextItem; + --m_Count; + ItemTypeTraits::AccessPrev(frontItem) = VMA_NULL; + ItemTypeTraits::AccessNext(frontItem) = VMA_NULL; + return frontItem; +} + +template +void VmaIntrusiveLinkedList::InsertBefore(ItemType* existingItem, ItemType* newItem) +{ + VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL); + if (existingItem != VMA_NULL) + { + ItemType* const prevItem = ItemTypeTraits::GetPrev(existingItem); + ItemTypeTraits::AccessPrev(newItem) = prevItem; + ItemTypeTraits::AccessNext(newItem) = existingItem; + ItemTypeTraits::AccessPrev(existingItem) = newItem; + if (prevItem != VMA_NULL) + { + ItemTypeTraits::AccessNext(prevItem) = newItem; + } + else + { + VMA_HEAVY_ASSERT(m_Front == existingItem); + m_Front = newItem; + } + ++m_Count; + } + else + PushBack(newItem); +} + +template +void VmaIntrusiveLinkedList::InsertAfter(ItemType* existingItem, ItemType* newItem) +{ + VMA_HEAVY_ASSERT(newItem != VMA_NULL && ItemTypeTraits::GetPrev(newItem) == VMA_NULL && ItemTypeTraits::GetNext(newItem) == VMA_NULL); + if (existingItem != VMA_NULL) + { + ItemType* const nextItem = ItemTypeTraits::GetNext(existingItem); + ItemTypeTraits::AccessNext(newItem) = nextItem; + ItemTypeTraits::AccessPrev(newItem) = existingItem; + ItemTypeTraits::AccessNext(existingItem) = newItem; + if (nextItem != VMA_NULL) + { + ItemTypeTraits::AccessPrev(nextItem) = newItem; + } + else + { + VMA_HEAVY_ASSERT(m_Back == existingItem); + m_Back = newItem; + } + ++m_Count; + } + else + return PushFront(newItem); +} + +template +void VmaIntrusiveLinkedList::Remove(ItemType* item) +{ + VMA_HEAVY_ASSERT(item != VMA_NULL && m_Count > 0); + if (ItemTypeTraits::GetPrev(item) != VMA_NULL) + { + ItemTypeTraits::AccessNext(ItemTypeTraits::AccessPrev(item)) = ItemTypeTraits::GetNext(item); + } + else + { + VMA_HEAVY_ASSERT(m_Front == item); + m_Front = ItemTypeTraits::GetNext(item); + } + + if (ItemTypeTraits::GetNext(item) != VMA_NULL) + { + ItemTypeTraits::AccessPrev(ItemTypeTraits::AccessNext(item)) = ItemTypeTraits::GetPrev(item); + } + else + { + VMA_HEAVY_ASSERT(m_Back == item); + m_Back = ItemTypeTraits::GetPrev(item); + } + ItemTypeTraits::AccessPrev(item) = VMA_NULL; + ItemTypeTraits::AccessNext(item) = VMA_NULL; + --m_Count; +} + +template +void VmaIntrusiveLinkedList::RemoveAll() +{ + if (!IsEmpty()) + { + ItemType* item = m_Back; + while (item != VMA_NULL) + { + ItemType* const prevItem = ItemTypeTraits::AccessPrev(item); + ItemTypeTraits::AccessPrev(item) = VMA_NULL; + ItemTypeTraits::AccessNext(item) = VMA_NULL; + item = prevItem; + } + m_Front = VMA_NULL; + m_Back = VMA_NULL; + m_Count = 0; + } +} +#endif // _VMA_INTRUSIVE_LINKED_LIST_FUNCTIONS +#endif // _VMA_INTRUSIVE_LINKED_LIST + +// Unused in this version. +#if 0 + +#ifndef _VMA_PAIR +template +struct VmaPair +{ + T1 first; + T2 second; + + VmaPair() : first(), second() {} + VmaPair(const T1& firstSrc, const T2& secondSrc) : first(firstSrc), second(secondSrc) {} +}; + +template +struct VmaPairFirstLess +{ + bool operator()(const VmaPair& lhs, const VmaPair& rhs) const + { + return lhs.first < rhs.first; + } + bool operator()(const VmaPair& lhs, const FirstT& rhsFirst) const + { + return lhs.first < rhsFirst; + } +}; +#endif // _VMA_PAIR + +#ifndef _VMA_MAP +/* Class compatible with subset of interface of std::unordered_map. +KeyT, ValueT must be POD because they will be stored in VmaVector. +*/ +template +class VmaMap +{ +public: + typedef VmaPair PairType; + typedef PairType* iterator; + + VmaMap(const VmaStlAllocator& allocator) : m_Vector(allocator) {} + + iterator begin() { return m_Vector.begin(); } + iterator end() { return m_Vector.end(); } + size_t size() { return m_Vector.size(); } + + void insert(const PairType& pair); + iterator find(const KeyT& key); + void erase(iterator it); + +private: + VmaVector< PairType, VmaStlAllocator> m_Vector; +}; + +#ifndef _VMA_MAP_FUNCTIONS +template +void VmaMap::insert(const PairType& pair) +{ + const size_t indexToInsert = VmaBinaryFindFirstNotLess( + m_Vector.data(), + m_Vector.data() + m_Vector.size(), + pair, + VmaPairFirstLess()) - m_Vector.data(); + VmaVectorInsert(m_Vector, indexToInsert, pair); +} + +template +VmaPair* VmaMap::find(const KeyT& key) +{ + PairType* it = VmaBinaryFindFirstNotLess( + m_Vector.data(), + m_Vector.data() + m_Vector.size(), + key, + VmaPairFirstLess()); + if ((it != m_Vector.end()) && (it->first == key)) + { + return it; + } + else + { + return m_Vector.end(); + } +} + +template +void VmaMap::erase(iterator it) +{ + VmaVectorRemove(m_Vector, it - m_Vector.begin()); +} +#endif // _VMA_MAP_FUNCTIONS +#endif // _VMA_MAP + +#endif // #if 0 + +#if !defined(_VMA_STRING_BUILDER) && VMA_STATS_STRING_ENABLED +class VmaStringBuilder +{ +public: + VmaStringBuilder(const VkAllocationCallbacks* allocationCallbacks) : m_Data(VmaStlAllocator(allocationCallbacks)) {} + ~VmaStringBuilder() = default; + + size_t GetLength() const { return m_Data.size(); } + const char* GetData() const { return m_Data.data(); } + void AddNewLine() { Add('\n'); } + void Add(char ch) { m_Data.push_back(ch); } + + void Add(const char* pStr); + void AddNumber(uint32_t num); + void AddNumber(uint64_t num); + void AddPointer(const void* ptr); + +private: + VmaVector> m_Data; +}; + +#ifndef _VMA_STRING_BUILDER_FUNCTIONS +void VmaStringBuilder::Add(const char* pStr) +{ + const size_t strLen = strlen(pStr); + if (strLen > 0) + { + const size_t oldCount = m_Data.size(); + m_Data.resize(oldCount + strLen); + memcpy(m_Data.data() + oldCount, pStr, strLen); + } +} + +void VmaStringBuilder::AddNumber(uint32_t num) +{ + char buf[11]; + buf[10] = '\0'; + char* p = &buf[10]; + do + { + *--p = '0' + (num % 10); + num /= 10; + } while (num); + Add(p); +} + +void VmaStringBuilder::AddNumber(uint64_t num) +{ + char buf[21]; + buf[20] = '\0'; + char* p = &buf[20]; + do + { + *--p = '0' + (num % 10); + num /= 10; + } while (num); + Add(p); +} + +void VmaStringBuilder::AddPointer(const void* ptr) +{ + char buf[21]; + VmaPtrToStr(buf, sizeof(buf), ptr); + Add(buf); +} +#endif //_VMA_STRING_BUILDER_FUNCTIONS +#endif // _VMA_STRING_BUILDER + +#if !defined(_VMA_JSON_WRITER) && VMA_STATS_STRING_ENABLED +/* +Allows to conveniently build a correct JSON document to be written to the +VmaStringBuilder passed to the constructor. +*/ +class VmaJsonWriter +{ + VMA_CLASS_NO_COPY(VmaJsonWriter) +public: + // sb - string builder to write the document to. Must remain alive for the whole lifetime of this object. + VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb); + ~VmaJsonWriter(); + + // Begins object by writing "{". + // Inside an object, you must call pairs of WriteString and a value, e.g.: + // j.BeginObject(true); j.WriteString("A"); j.WriteNumber(1); j.WriteString("B"); j.WriteNumber(2); j.EndObject(); + // Will write: { "A": 1, "B": 2 } + void BeginObject(bool singleLine = false); + // Ends object by writing "}". + void EndObject(); + + // Begins array by writing "[". + // Inside an array, you can write a sequence of any values. + void BeginArray(bool singleLine = false); + // Ends array by writing "[". + void EndArray(); + + // Writes a string value inside "". + // pStr can contain any ANSI characters, including '"', new line etc. - they will be properly escaped. + void WriteString(const char* pStr); + + // Begins writing a string value. + // Call BeginString, ContinueString, ContinueString, ..., EndString instead of + // WriteString to conveniently build the string content incrementally, made of + // parts including numbers. + void BeginString(const char* pStr = VMA_NULL); + // Posts next part of an open string. + void ContinueString(const char* pStr); + // Posts next part of an open string. The number is converted to decimal characters. + void ContinueString(uint32_t n); + void ContinueString(uint64_t n); + // Posts next part of an open string. Pointer value is converted to characters + // using "%p" formatting - shown as hexadecimal number, e.g.: 000000081276Ad00 + void ContinueString_Pointer(const void* ptr); + // Ends writing a string value by writing '"'. + void EndString(const char* pStr = VMA_NULL); + + // Writes a number value. + void WriteNumber(uint32_t n); + void WriteNumber(uint64_t n); + // Writes a boolean value - false or true. + void WriteBool(bool b); + // Writes a null value. + void WriteNull(); + +private: + enum COLLECTION_TYPE + { + COLLECTION_TYPE_OBJECT, + COLLECTION_TYPE_ARRAY, + }; + struct StackItem + { + COLLECTION_TYPE type; + uint32_t valueCount; + bool singleLineMode; + }; + + static const char* const INDENT; + + VmaStringBuilder& m_SB; + VmaVector< StackItem, VmaStlAllocator > m_Stack; + bool m_InsideString; + + void BeginValue(bool isString); + void WriteIndent(bool oneLess = false); +}; +const char* const VmaJsonWriter::INDENT = " "; + +#ifndef _VMA_JSON_WRITER_FUNCTIONS +VmaJsonWriter::VmaJsonWriter(const VkAllocationCallbacks* pAllocationCallbacks, VmaStringBuilder& sb) + : m_SB(sb), + m_Stack(VmaStlAllocator(pAllocationCallbacks)), + m_InsideString(false) {} + +VmaJsonWriter::~VmaJsonWriter() +{ + VMA_ASSERT(!m_InsideString); + VMA_ASSERT(m_Stack.empty()); +} + +void VmaJsonWriter::BeginObject(bool singleLine) +{ + VMA_ASSERT(!m_InsideString); + + BeginValue(false); + m_SB.Add('{'); + + StackItem item; + item.type = COLLECTION_TYPE_OBJECT; + item.valueCount = 0; + item.singleLineMode = singleLine; + m_Stack.push_back(item); +} + +void VmaJsonWriter::EndObject() +{ + VMA_ASSERT(!m_InsideString); + + WriteIndent(true); + m_SB.Add('}'); + + VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_OBJECT); + m_Stack.pop_back(); +} + +void VmaJsonWriter::BeginArray(bool singleLine) +{ + VMA_ASSERT(!m_InsideString); + + BeginValue(false); + m_SB.Add('['); + + StackItem item; + item.type = COLLECTION_TYPE_ARRAY; + item.valueCount = 0; + item.singleLineMode = singleLine; + m_Stack.push_back(item); +} + +void VmaJsonWriter::EndArray() +{ + VMA_ASSERT(!m_InsideString); + + WriteIndent(true); + m_SB.Add(']'); + + VMA_ASSERT(!m_Stack.empty() && m_Stack.back().type == COLLECTION_TYPE_ARRAY); + m_Stack.pop_back(); +} + +void VmaJsonWriter::WriteString(const char* pStr) +{ + BeginString(pStr); + EndString(); +} + +void VmaJsonWriter::BeginString(const char* pStr) +{ + VMA_ASSERT(!m_InsideString); + + BeginValue(true); + m_SB.Add('"'); + m_InsideString = true; + if (pStr != VMA_NULL && pStr[0] != '\0') + { + ContinueString(pStr); + } +} + +void VmaJsonWriter::ContinueString(const char* pStr) +{ + VMA_ASSERT(m_InsideString); + + const size_t strLen = strlen(pStr); + for (size_t i = 0; i < strLen; ++i) + { + char ch = pStr[i]; + if (ch == '\\') + { + m_SB.Add("\\\\"); + } + else if (ch == '"') + { + m_SB.Add("\\\""); + } + else if (ch >= 32) + { + m_SB.Add(ch); + } + else switch (ch) + { + case '\b': + m_SB.Add("\\b"); + break; + case '\f': + m_SB.Add("\\f"); + break; + case '\n': + m_SB.Add("\\n"); + break; + case '\r': + m_SB.Add("\\r"); + break; + case '\t': + m_SB.Add("\\t"); + break; + default: + VMA_ASSERT(0 && "Character not currently supported."); + break; + } + } +} + +void VmaJsonWriter::ContinueString(uint32_t n) +{ + VMA_ASSERT(m_InsideString); + m_SB.AddNumber(n); +} + +void VmaJsonWriter::ContinueString(uint64_t n) +{ + VMA_ASSERT(m_InsideString); + m_SB.AddNumber(n); +} + +void VmaJsonWriter::ContinueString_Pointer(const void* ptr) +{ + VMA_ASSERT(m_InsideString); + m_SB.AddPointer(ptr); +} + +void VmaJsonWriter::EndString(const char* pStr) +{ + VMA_ASSERT(m_InsideString); + if (pStr != VMA_NULL && pStr[0] != '\0') + { + ContinueString(pStr); + } + m_SB.Add('"'); + m_InsideString = false; +} + +void VmaJsonWriter::WriteNumber(uint32_t n) +{ + VMA_ASSERT(!m_InsideString); + BeginValue(false); + m_SB.AddNumber(n); +} + +void VmaJsonWriter::WriteNumber(uint64_t n) +{ + VMA_ASSERT(!m_InsideString); + BeginValue(false); + m_SB.AddNumber(n); +} + +void VmaJsonWriter::WriteBool(bool b) +{ + VMA_ASSERT(!m_InsideString); + BeginValue(false); + m_SB.Add(b ? "true" : "false"); +} + +void VmaJsonWriter::WriteNull() +{ + VMA_ASSERT(!m_InsideString); + BeginValue(false); + m_SB.Add("null"); +} + +void VmaJsonWriter::BeginValue(bool isString) +{ + if (!m_Stack.empty()) + { + StackItem& currItem = m_Stack.back(); + if (currItem.type == COLLECTION_TYPE_OBJECT && + currItem.valueCount % 2 == 0) + { + VMA_ASSERT(isString); + } + + if (currItem.type == COLLECTION_TYPE_OBJECT && + currItem.valueCount % 2 != 0) + { + m_SB.Add(": "); + } + else if (currItem.valueCount > 0) + { + m_SB.Add(", "); + WriteIndent(); + } + else + { + WriteIndent(); + } + ++currItem.valueCount; + } +} + +void VmaJsonWriter::WriteIndent(bool oneLess) +{ + if (!m_Stack.empty() && !m_Stack.back().singleLineMode) + { + m_SB.AddNewLine(); + + size_t count = m_Stack.size(); + if (count > 0 && oneLess) + { + --count; + } + for (size_t i = 0; i < count; ++i) + { + m_SB.Add(INDENT); + } + } +} +#endif // _VMA_JSON_WRITER_FUNCTIONS + +static void VmaPrintDetailedStatistics(VmaJsonWriter& json, const VmaDetailedStatistics& stat) +{ + json.BeginObject(); + + json.WriteString("BlockCount"); + json.WriteNumber(stat.statistics.blockCount); + json.WriteString("BlockBytes"); + json.WriteNumber(stat.statistics.blockBytes); + json.WriteString("AllocationCount"); + json.WriteNumber(stat.statistics.allocationCount); + json.WriteString("AllocationBytes"); + json.WriteNumber(stat.statistics.allocationBytes); + json.WriteString("UnusedRangeCount"); + json.WriteNumber(stat.unusedRangeCount); + + if (stat.statistics.allocationCount > 1) + { + json.WriteString("AllocationSizeMin"); + json.WriteNumber(stat.allocationSizeMin); + json.WriteString("AllocationSizeMax"); + json.WriteNumber(stat.allocationSizeMax); + } + if (stat.unusedRangeCount > 1) + { + json.WriteString("UnusedRangeSizeMin"); + json.WriteNumber(stat.unusedRangeSizeMin); + json.WriteString("UnusedRangeSizeMax"); + json.WriteNumber(stat.unusedRangeSizeMax); + } + json.EndObject(); +} +#endif // _VMA_JSON_WRITER + +#ifndef _VMA_MAPPING_HYSTERESIS + +class VmaMappingHysteresis +{ + VMA_CLASS_NO_COPY(VmaMappingHysteresis) +public: + VmaMappingHysteresis() = default; + + uint32_t GetExtraMapping() const { return m_ExtraMapping; } + + // Call when Map was called. + // Returns true if switched to extra +1 mapping reference count. + bool PostMap() + { +#if VMA_MAPPING_HYSTERESIS_ENABLED + if(m_ExtraMapping == 0) + { + ++m_MajorCounter; + if(m_MajorCounter >= COUNTER_MIN_EXTRA_MAPPING) + { + m_ExtraMapping = 1; + m_MajorCounter = 0; + m_MinorCounter = 0; + return true; + } + } + else // m_ExtraMapping == 1 + PostMinorCounter(); +#endif // #if VMA_MAPPING_HYSTERESIS_ENABLED + return false; + } + + // Call when Unmap was called. + void PostUnmap() + { +#if VMA_MAPPING_HYSTERESIS_ENABLED + if(m_ExtraMapping == 0) + ++m_MajorCounter; + else // m_ExtraMapping == 1 + PostMinorCounter(); +#endif // #if VMA_MAPPING_HYSTERESIS_ENABLED + } + + // Call when allocation was made from the memory block. + void PostAlloc() + { +#if VMA_MAPPING_HYSTERESIS_ENABLED + if(m_ExtraMapping == 1) + ++m_MajorCounter; + else // m_ExtraMapping == 0 + PostMinorCounter(); +#endif // #if VMA_MAPPING_HYSTERESIS_ENABLED + } + + // Call when allocation was freed from the memory block. + // Returns true if switched to extra -1 mapping reference count. + bool PostFree() + { +#if VMA_MAPPING_HYSTERESIS_ENABLED + if(m_ExtraMapping == 1) + { + ++m_MajorCounter; + if(m_MajorCounter >= COUNTER_MIN_EXTRA_MAPPING && + m_MajorCounter > m_MinorCounter + 1) + { + m_ExtraMapping = 0; + m_MajorCounter = 0; + m_MinorCounter = 0; + return true; + } + } + else // m_ExtraMapping == 0 + PostMinorCounter(); +#endif // #if VMA_MAPPING_HYSTERESIS_ENABLED + return false; + } + +private: + static const int32_t COUNTER_MIN_EXTRA_MAPPING = 7; + + uint32_t m_MinorCounter = 0; + uint32_t m_MajorCounter = 0; + uint32_t m_ExtraMapping = 0; // 0 or 1. + + void PostMinorCounter() + { + if(m_MinorCounter < m_MajorCounter) + { + ++m_MinorCounter; + } + else if(m_MajorCounter > 0) + { + --m_MajorCounter; + --m_MinorCounter; + } + } +}; + +#endif // _VMA_MAPPING_HYSTERESIS + +#ifndef _VMA_DEVICE_MEMORY_BLOCK +/* +Represents a single block of device memory (`VkDeviceMemory`) with all the +data about its regions (aka suballocations, #VmaAllocation), assigned and free. + +Thread-safety: +- Access to m_pMetadata must be externally synchronized. +- Map, Unmap, Bind* are synchronized internally. +*/ +class VmaDeviceMemoryBlock +{ + VMA_CLASS_NO_COPY(VmaDeviceMemoryBlock) +public: + VmaBlockMetadata* m_pMetadata; + + VmaDeviceMemoryBlock(VmaAllocator hAllocator); + ~VmaDeviceMemoryBlock(); + + // Always call after construction. + void Init( + VmaAllocator hAllocator, + VmaPool hParentPool, + uint32_t newMemoryTypeIndex, + VkDeviceMemory newMemory, + VkDeviceSize newSize, + uint32_t id, + uint32_t algorithm, + VkDeviceSize bufferImageGranularity); + // Always call before destruction. + void Destroy(VmaAllocator allocator); + + VmaPool GetParentPool() const { return m_hParentPool; } + VkDeviceMemory GetDeviceMemory() const { return m_hMemory; } + uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; } + uint32_t GetId() const { return m_Id; } + void* GetMappedData() const { return m_pMappedData; } + uint32_t GetMapRefCount() const { return m_MapCount; } + + // Call when allocation/free was made from m_pMetadata. + // Used for m_MappingHysteresis. + void PostAlloc(VmaAllocator hAllocator); + void PostFree(VmaAllocator hAllocator); + + // Validates all data structures inside this object. If not valid, returns false. + bool Validate() const; + VkResult CheckCorruption(VmaAllocator hAllocator); + + // ppData can be null. + VkResult Map(VmaAllocator hAllocator, uint32_t count, void** ppData); + void Unmap(VmaAllocator hAllocator, uint32_t count); + + VkResult WriteMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize); + VkResult ValidateMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize); + + VkResult BindBufferMemory( + const VmaAllocator hAllocator, + const VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkBuffer hBuffer, + const void* pNext); + VkResult BindImageMemory( + const VmaAllocator hAllocator, + const VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkImage hImage, + const void* pNext); + +private: + VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool. + uint32_t m_MemoryTypeIndex; + uint32_t m_Id; + VkDeviceMemory m_hMemory; + + /* + Protects access to m_hMemory so it is not used by multiple threads simultaneously, e.g. vkMapMemory, vkBindBufferMemory. + Also protects m_MapCount, m_pMappedData. + Allocations, deallocations, any change in m_pMetadata is protected by parent's VmaBlockVector::m_Mutex. + */ + VMA_MUTEX m_MapAndBindMutex; + VmaMappingHysteresis m_MappingHysteresis; + uint32_t m_MapCount; + void* m_pMappedData; +}; +#endif // _VMA_DEVICE_MEMORY_BLOCK + +#ifndef _VMA_ALLOCATION_T +struct VmaAllocation_T +{ + friend struct VmaDedicatedAllocationListItemTraits; + + enum FLAGS + { + FLAG_PERSISTENT_MAP = 0x01, + FLAG_MAPPING_ALLOWED = 0x02, + }; + +public: + enum ALLOCATION_TYPE + { + ALLOCATION_TYPE_NONE, + ALLOCATION_TYPE_BLOCK, + ALLOCATION_TYPE_DEDICATED, + }; + + // This struct is allocated using VmaPoolAllocator. + VmaAllocation_T(bool mappingAllowed); + ~VmaAllocation_T(); + + void InitBlockAllocation( + VmaDeviceMemoryBlock* block, + VmaAllocHandle allocHandle, + VkDeviceSize alignment, + VkDeviceSize size, + uint32_t memoryTypeIndex, + VmaSuballocationType suballocationType, + bool mapped); + // pMappedData not null means allocation is created with MAPPED flag. + void InitDedicatedAllocation( + VmaPool hParentPool, + uint32_t memoryTypeIndex, + VkDeviceMemory hMemory, + VmaSuballocationType suballocationType, + void* pMappedData, + VkDeviceSize size); + + ALLOCATION_TYPE GetType() const { return (ALLOCATION_TYPE)m_Type; } + VkDeviceSize GetAlignment() const { return m_Alignment; } + VkDeviceSize GetSize() const { return m_Size; } + void* GetUserData() const { return m_pUserData; } + const char* GetName() const { return m_pName; } + VmaSuballocationType GetSuballocationType() const { return (VmaSuballocationType)m_SuballocationType; } + + VmaDeviceMemoryBlock* GetBlock() const { VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); return m_BlockAllocation.m_Block; } + uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; } + bool IsPersistentMap() const { return (m_Flags & FLAG_PERSISTENT_MAP) != 0; } + bool IsMappingAllowed() const { return (m_Flags & FLAG_MAPPING_ALLOWED) != 0; } + + void SetUserData(VmaAllocator hAllocator, void* pUserData) { m_pUserData = pUserData; } + void SetName(VmaAllocator hAllocator, const char* pName); + void FreeName(VmaAllocator hAllocator); + uint8_t SwapBlockAllocation(VmaAllocator hAllocator, VmaAllocation allocation); + VmaAllocHandle GetAllocHandle() const; + VkDeviceSize GetOffset() const; + VmaPool GetParentPool() const; + VkDeviceMemory GetMemory() const; + void* GetMappedData() const; + + void BlockAllocMap(); + void BlockAllocUnmap(); + VkResult DedicatedAllocMap(VmaAllocator hAllocator, void** ppData); + void DedicatedAllocUnmap(VmaAllocator hAllocator); + +#if VMA_STATS_STRING_ENABLED + uint32_t GetBufferImageUsage() const { return m_BufferImageUsage; } + + void InitBufferImageUsage(uint32_t bufferImageUsage); + void PrintParameters(class VmaJsonWriter& json) const; +#endif + +private: + // Allocation out of VmaDeviceMemoryBlock. + struct BlockAllocation + { + VmaDeviceMemoryBlock* m_Block; + VmaAllocHandle m_AllocHandle; + }; + // Allocation for an object that has its own private VkDeviceMemory. + struct DedicatedAllocation + { + VmaPool m_hParentPool; // VK_NULL_HANDLE if not belongs to custom pool. + VkDeviceMemory m_hMemory; + void* m_pMappedData; // Not null means memory is mapped. + VmaAllocation_T* m_Prev; + VmaAllocation_T* m_Next; + }; + union + { + // Allocation out of VmaDeviceMemoryBlock. + BlockAllocation m_BlockAllocation; + // Allocation for an object that has its own private VkDeviceMemory. + DedicatedAllocation m_DedicatedAllocation; + }; + + VkDeviceSize m_Alignment; + VkDeviceSize m_Size; + void* m_pUserData; + char* m_pName; + uint32_t m_MemoryTypeIndex; + uint8_t m_Type; // ALLOCATION_TYPE + uint8_t m_SuballocationType; // VmaSuballocationType + // Reference counter for vmaMapMemory()/vmaUnmapMemory(). + uint8_t m_MapCount; + uint8_t m_Flags; // enum FLAGS +#if VMA_STATS_STRING_ENABLED + uint32_t m_BufferImageUsage; // 0 if unknown. +#endif +}; +#endif // _VMA_ALLOCATION_T + +#ifndef _VMA_DEDICATED_ALLOCATION_LIST_ITEM_TRAITS +struct VmaDedicatedAllocationListItemTraits +{ + typedef VmaAllocation_T ItemType; + + static ItemType* GetPrev(const ItemType* item) + { + VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); + return item->m_DedicatedAllocation.m_Prev; + } + static ItemType* GetNext(const ItemType* item) + { + VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); + return item->m_DedicatedAllocation.m_Next; + } + static ItemType*& AccessPrev(ItemType* item) + { + VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); + return item->m_DedicatedAllocation.m_Prev; + } + static ItemType*& AccessNext(ItemType* item) + { + VMA_HEAVY_ASSERT(item->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); + return item->m_DedicatedAllocation.m_Next; + } +}; +#endif // _VMA_DEDICATED_ALLOCATION_LIST_ITEM_TRAITS + +#ifndef _VMA_DEDICATED_ALLOCATION_LIST +/* +Stores linked list of VmaAllocation_T objects. +Thread-safe, synchronized internally. +*/ +class VmaDedicatedAllocationList +{ +public: + VmaDedicatedAllocationList() {} + ~VmaDedicatedAllocationList(); + + void Init(bool useMutex) { m_UseMutex = useMutex; } + bool Validate(); + + void AddDetailedStatistics(VmaDetailedStatistics& inoutStats); + void AddStatistics(VmaStatistics& inoutStats); +#if VMA_STATS_STRING_ENABLED + // Writes JSON array with the list of allocations. + void BuildStatsString(VmaJsonWriter& json); +#endif + + bool IsEmpty(); + void Register(VmaAllocation alloc); + void Unregister(VmaAllocation alloc); + +private: + typedef VmaIntrusiveLinkedList DedicatedAllocationLinkedList; + + bool m_UseMutex = true; + VMA_RW_MUTEX m_Mutex; + DedicatedAllocationLinkedList m_AllocationList; +}; + +#ifndef _VMA_DEDICATED_ALLOCATION_LIST_FUNCTIONS + +VmaDedicatedAllocationList::~VmaDedicatedAllocationList() +{ + VMA_HEAVY_ASSERT(Validate()); + + if (!m_AllocationList.IsEmpty()) + { + VMA_ASSERT(false && "Unfreed dedicated allocations found!"); + } +} + +bool VmaDedicatedAllocationList::Validate() +{ + const size_t declaredCount = m_AllocationList.GetCount(); + size_t actualCount = 0; + VmaMutexLockRead lock(m_Mutex, m_UseMutex); + for (VmaAllocation alloc = m_AllocationList.Front(); + alloc != VMA_NULL; alloc = m_AllocationList.GetNext(alloc)) + { + ++actualCount; + } + VMA_VALIDATE(actualCount == declaredCount); + + return true; +} + +void VmaDedicatedAllocationList::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) +{ + for(auto* item = m_AllocationList.Front(); item != nullptr; item = DedicatedAllocationLinkedList::GetNext(item)) + { + const VkDeviceSize size = item->GetSize(); + inoutStats.statistics.blockCount++; + inoutStats.statistics.blockBytes += size; + VmaAddDetailedStatisticsAllocation(inoutStats, item->GetSize()); + } +} + +void VmaDedicatedAllocationList::AddStatistics(VmaStatistics& inoutStats) +{ + VmaMutexLockRead lock(m_Mutex, m_UseMutex); + + const uint32_t allocCount = (uint32_t)m_AllocationList.GetCount(); + inoutStats.blockCount += allocCount; + inoutStats.allocationCount += allocCount; + + for(auto* item = m_AllocationList.Front(); item != nullptr; item = DedicatedAllocationLinkedList::GetNext(item)) + { + const VkDeviceSize size = item->GetSize(); + inoutStats.blockBytes += size; + inoutStats.allocationBytes += size; + } +} + +#if VMA_STATS_STRING_ENABLED +void VmaDedicatedAllocationList::BuildStatsString(VmaJsonWriter& json) +{ + VmaMutexLockRead lock(m_Mutex, m_UseMutex); + json.BeginArray(); + for (VmaAllocation alloc = m_AllocationList.Front(); + alloc != VMA_NULL; alloc = m_AllocationList.GetNext(alloc)) + { + json.BeginObject(true); + alloc->PrintParameters(json); + json.EndObject(); + } + json.EndArray(); +} +#endif // VMA_STATS_STRING_ENABLED + +bool VmaDedicatedAllocationList::IsEmpty() +{ + VmaMutexLockRead lock(m_Mutex, m_UseMutex); + return m_AllocationList.IsEmpty(); +} + +void VmaDedicatedAllocationList::Register(VmaAllocation alloc) +{ + VmaMutexLockWrite lock(m_Mutex, m_UseMutex); + m_AllocationList.PushBack(alloc); +} + +void VmaDedicatedAllocationList::Unregister(VmaAllocation alloc) +{ + VmaMutexLockWrite lock(m_Mutex, m_UseMutex); + m_AllocationList.Remove(alloc); +} +#endif // _VMA_DEDICATED_ALLOCATION_LIST_FUNCTIONS +#endif // _VMA_DEDICATED_ALLOCATION_LIST + +#ifndef _VMA_SUBALLOCATION +/* +Represents a region of VmaDeviceMemoryBlock that is either assigned and returned as +allocated memory block or free. +*/ +struct VmaSuballocation +{ + VkDeviceSize offset; + VkDeviceSize size; + void* userData; + VmaSuballocationType type; +}; + +// Comparator for offsets. +struct VmaSuballocationOffsetLess +{ + bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const + { + return lhs.offset < rhs.offset; + } +}; + +struct VmaSuballocationOffsetGreater +{ + bool operator()(const VmaSuballocation& lhs, const VmaSuballocation& rhs) const + { + return lhs.offset > rhs.offset; + } +}; + +struct VmaSuballocationItemSizeLess +{ + bool operator()(const VmaSuballocationList::iterator lhs, + const VmaSuballocationList::iterator rhs) const + { + return lhs->size < rhs->size; + } + + bool operator()(const VmaSuballocationList::iterator lhs, + VkDeviceSize rhsSize) const + { + return lhs->size < rhsSize; + } +}; +#endif // _VMA_SUBALLOCATION + +#ifndef _VMA_ALLOCATION_REQUEST +/* +Parameters of planned allocation inside a VmaDeviceMemoryBlock. +item points to a FREE suballocation. +*/ +struct VmaAllocationRequest +{ + VmaAllocHandle allocHandle; + VkDeviceSize size; + VmaSuballocationList::iterator item; + void* customData; + uint64_t algorithmData; + VmaAllocationRequestType type; +}; +#endif // _VMA_ALLOCATION_REQUEST + +#ifndef _VMA_BLOCK_METADATA +/* +Data structure used for bookkeeping of allocations and unused ranges of memory +in a single VkDeviceMemory block. +*/ +class VmaBlockMetadata +{ +public: + // pAllocationCallbacks, if not null, must be owned externally - alive and unchanged for the whole lifetime of this object. + VmaBlockMetadata(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual); + virtual ~VmaBlockMetadata() = default; + + virtual void Init(VkDeviceSize size) { m_Size = size; } + bool IsVirtual() const { return m_IsVirtual; } + VkDeviceSize GetSize() const { return m_Size; } + + // Validates all data structures inside this object. If not valid, returns false. + virtual bool Validate() const = 0; + virtual size_t GetAllocationCount() const = 0; + virtual size_t GetFreeRegionsCount() const = 0; + virtual VkDeviceSize GetSumFreeSize() const = 0; + // Returns true if this block is empty - contains only single free suballocation. + virtual bool IsEmpty() const = 0; + virtual void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) = 0; + virtual VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const = 0; + virtual void* GetAllocationUserData(VmaAllocHandle allocHandle) const = 0; + + virtual VmaAllocHandle GetAllocationListBegin() const = 0; + virtual VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const = 0; + virtual VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const = 0; + + // Shouldn't modify blockCount. + virtual void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const = 0; + virtual void AddStatistics(VmaStatistics& inoutStats) const = 0; + +#if VMA_STATS_STRING_ENABLED + virtual void PrintDetailedMap(class VmaJsonWriter& json) const = 0; +#endif + + // Tries to find a place for suballocation with given parameters inside this block. + // If succeeded, fills pAllocationRequest and returns true. + // If failed, returns false. + virtual bool CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + // Always one of VMA_ALLOCATION_CREATE_STRATEGY_* or VMA_ALLOCATION_INTERNAL_STRATEGY_* flags. + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) = 0; + + virtual VkResult CheckCorruption(const void* pBlockData) = 0; + + // Makes actual allocation based on request. Request must already be checked and valid. + virtual void Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) = 0; + + // Frees suballocation assigned to given memory region. + virtual void Free(VmaAllocHandle allocHandle) = 0; + + // Frees all allocations. + // Careful! Don't call it if there are VmaAllocation objects owned by userData of cleared allocations! + virtual void Clear() = 0; + + virtual void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) = 0; + virtual void DebugLogAllAllocations() const = 0; + +protected: + const VkAllocationCallbacks* GetAllocationCallbacks() const { return m_pAllocationCallbacks; } + VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; } + VkDeviceSize GetDebugMargin() const { return IsVirtual() ? 0 : VMA_DEBUG_MARGIN; } + + void DebugLogAllocation(VkDeviceSize offset, VkDeviceSize size, void* userData) const; +#if VMA_STATS_STRING_ENABLED + // mapRefCount == UINT32_MAX means unspecified. + void PrintDetailedMap_Begin(class VmaJsonWriter& json, + VkDeviceSize unusedBytes, + size_t allocationCount, + size_t unusedRangeCount) const; + void PrintDetailedMap_Allocation(class VmaJsonWriter& json, + VkDeviceSize offset, VkDeviceSize size, void* userData) const; + void PrintDetailedMap_UnusedRange(class VmaJsonWriter& json, + VkDeviceSize offset, + VkDeviceSize size) const; + void PrintDetailedMap_End(class VmaJsonWriter& json) const; +#endif + +private: + VkDeviceSize m_Size; + const VkAllocationCallbacks* m_pAllocationCallbacks; + const VkDeviceSize m_BufferImageGranularity; + const bool m_IsVirtual; +}; + +#ifndef _VMA_BLOCK_METADATA_FUNCTIONS +VmaBlockMetadata::VmaBlockMetadata(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual) + : m_Size(0), + m_pAllocationCallbacks(pAllocationCallbacks), + m_BufferImageGranularity(bufferImageGranularity), + m_IsVirtual(isVirtual) {} + +void VmaBlockMetadata::DebugLogAllocation(VkDeviceSize offset, VkDeviceSize size, void* userData) const +{ + if (IsVirtual()) + { + VMA_DEBUG_LOG_FORMAT("UNFREED VIRTUAL ALLOCATION; Offset: %llu; Size: %llu; UserData: %p", offset, size, userData); + } + else + { + VMA_ASSERT(userData != VMA_NULL); + VmaAllocation allocation = reinterpret_cast(userData); + + userData = allocation->GetUserData(); + const char* name = allocation->GetName(); + +#if VMA_STATS_STRING_ENABLED + VMA_DEBUG_LOG_FORMAT("UNFREED ALLOCATION; Offset: %llu; Size: %llu; UserData: %p; Name: %s; Type: %s; Usage: %u", + offset, size, userData, name ? name : "vma_empty", + VMA_SUBALLOCATION_TYPE_NAMES[allocation->GetSuballocationType()], + allocation->GetBufferImageUsage()); +#else + VMA_DEBUG_LOG_FORMAT("UNFREED ALLOCATION; Offset: %llu; Size: %llu; UserData: %p; Name: %s; Type: %u", + offset, size, userData, name ? name : "vma_empty", + (uint32_t)allocation->GetSuballocationType()); +#endif // VMA_STATS_STRING_ENABLED + } + +} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockMetadata::PrintDetailedMap_Begin(class VmaJsonWriter& json, + VkDeviceSize unusedBytes, size_t allocationCount, size_t unusedRangeCount) const +{ + json.WriteString("TotalBytes"); + json.WriteNumber(GetSize()); + + json.WriteString("UnusedBytes"); + json.WriteNumber(unusedBytes); + + json.WriteString("Allocations"); + json.WriteNumber((uint64_t)allocationCount); + + json.WriteString("UnusedRanges"); + json.WriteNumber((uint64_t)unusedRangeCount); + + json.WriteString("Suballocations"); + json.BeginArray(); +} + +void VmaBlockMetadata::PrintDetailedMap_Allocation(class VmaJsonWriter& json, + VkDeviceSize offset, VkDeviceSize size, void* userData) const +{ + json.BeginObject(true); + + json.WriteString("Offset"); + json.WriteNumber(offset); + + if (IsVirtual()) + { + json.WriteString("Size"); + json.WriteNumber(size); + if (userData) + { + json.WriteString("CustomData"); + json.BeginString(); + json.ContinueString_Pointer(userData); + json.EndString(); + } + } + else + { + ((VmaAllocation)userData)->PrintParameters(json); + } + + json.EndObject(); +} + +void VmaBlockMetadata::PrintDetailedMap_UnusedRange(class VmaJsonWriter& json, + VkDeviceSize offset, VkDeviceSize size) const +{ + json.BeginObject(true); + + json.WriteString("Offset"); + json.WriteNumber(offset); + + json.WriteString("Type"); + json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[VMA_SUBALLOCATION_TYPE_FREE]); + + json.WriteString("Size"); + json.WriteNumber(size); + + json.EndObject(); +} + +void VmaBlockMetadata::PrintDetailedMap_End(class VmaJsonWriter& json) const +{ + json.EndArray(); +} +#endif // VMA_STATS_STRING_ENABLED +#endif // _VMA_BLOCK_METADATA_FUNCTIONS +#endif // _VMA_BLOCK_METADATA + +#ifndef _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY +// Before deleting object of this class remember to call 'Destroy()' +class VmaBlockBufferImageGranularity final +{ +public: + struct ValidationContext + { + const VkAllocationCallbacks* allocCallbacks; + uint16_t* pageAllocs; + }; + + VmaBlockBufferImageGranularity(VkDeviceSize bufferImageGranularity); + ~VmaBlockBufferImageGranularity(); + + bool IsEnabled() const { return m_BufferImageGranularity > MAX_LOW_BUFFER_IMAGE_GRANULARITY; } + + void Init(const VkAllocationCallbacks* pAllocationCallbacks, VkDeviceSize size); + // Before destroying object you must call free it's memory + void Destroy(const VkAllocationCallbacks* pAllocationCallbacks); + + void RoundupAllocRequest(VmaSuballocationType allocType, + VkDeviceSize& inOutAllocSize, + VkDeviceSize& inOutAllocAlignment) const; + + bool CheckConflictAndAlignUp(VkDeviceSize& inOutAllocOffset, + VkDeviceSize allocSize, + VkDeviceSize blockOffset, + VkDeviceSize blockSize, + VmaSuballocationType allocType) const; + + void AllocPages(uint8_t allocType, VkDeviceSize offset, VkDeviceSize size); + void FreePages(VkDeviceSize offset, VkDeviceSize size); + void Clear(); + + ValidationContext StartValidation(const VkAllocationCallbacks* pAllocationCallbacks, + bool isVirutal) const; + bool Validate(ValidationContext& ctx, VkDeviceSize offset, VkDeviceSize size) const; + bool FinishValidation(ValidationContext& ctx) const; + +private: + static const uint16_t MAX_LOW_BUFFER_IMAGE_GRANULARITY = 256; + + struct RegionInfo + { + uint8_t allocType; + uint16_t allocCount; + }; + + VkDeviceSize m_BufferImageGranularity; + uint32_t m_RegionCount; + RegionInfo* m_RegionInfo; + + uint32_t GetStartPage(VkDeviceSize offset) const { return OffsetToPageIndex(offset & ~(m_BufferImageGranularity - 1)); } + uint32_t GetEndPage(VkDeviceSize offset, VkDeviceSize size) const { return OffsetToPageIndex((offset + size - 1) & ~(m_BufferImageGranularity - 1)); } + + uint32_t OffsetToPageIndex(VkDeviceSize offset) const; + void AllocPage(RegionInfo& page, uint8_t allocType); +}; + +#ifndef _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY_FUNCTIONS +VmaBlockBufferImageGranularity::VmaBlockBufferImageGranularity(VkDeviceSize bufferImageGranularity) + : m_BufferImageGranularity(bufferImageGranularity), + m_RegionCount(0), + m_RegionInfo(VMA_NULL) {} + +VmaBlockBufferImageGranularity::~VmaBlockBufferImageGranularity() +{ + VMA_ASSERT(m_RegionInfo == VMA_NULL && "Free not called before destroying object!"); +} + +void VmaBlockBufferImageGranularity::Init(const VkAllocationCallbacks* pAllocationCallbacks, VkDeviceSize size) +{ + if (IsEnabled()) + { + m_RegionCount = static_cast(VmaDivideRoundingUp(size, m_BufferImageGranularity)); + m_RegionInfo = vma_new_array(pAllocationCallbacks, RegionInfo, m_RegionCount); + memset(m_RegionInfo, 0, m_RegionCount * sizeof(RegionInfo)); + } +} + +void VmaBlockBufferImageGranularity::Destroy(const VkAllocationCallbacks* pAllocationCallbacks) +{ + if (m_RegionInfo) + { + vma_delete_array(pAllocationCallbacks, m_RegionInfo, m_RegionCount); + m_RegionInfo = VMA_NULL; + } +} + +void VmaBlockBufferImageGranularity::RoundupAllocRequest(VmaSuballocationType allocType, + VkDeviceSize& inOutAllocSize, + VkDeviceSize& inOutAllocAlignment) const +{ + if (m_BufferImageGranularity > 1 && + m_BufferImageGranularity <= MAX_LOW_BUFFER_IMAGE_GRANULARITY) + { + if (allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN || + allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN || + allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL) + { + inOutAllocAlignment = VMA_MAX(inOutAllocAlignment, m_BufferImageGranularity); + inOutAllocSize = VmaAlignUp(inOutAllocSize, m_BufferImageGranularity); + } + } +} + +bool VmaBlockBufferImageGranularity::CheckConflictAndAlignUp(VkDeviceSize& inOutAllocOffset, + VkDeviceSize allocSize, + VkDeviceSize blockOffset, + VkDeviceSize blockSize, + VmaSuballocationType allocType) const +{ + if (IsEnabled()) + { + uint32_t startPage = GetStartPage(inOutAllocOffset); + if (m_RegionInfo[startPage].allocCount > 0 && + VmaIsBufferImageGranularityConflict(static_cast(m_RegionInfo[startPage].allocType), allocType)) + { + inOutAllocOffset = VmaAlignUp(inOutAllocOffset, m_BufferImageGranularity); + if (blockSize < allocSize + inOutAllocOffset - blockOffset) + return true; + ++startPage; + } + uint32_t endPage = GetEndPage(inOutAllocOffset, allocSize); + if (endPage != startPage && + m_RegionInfo[endPage].allocCount > 0 && + VmaIsBufferImageGranularityConflict(static_cast(m_RegionInfo[endPage].allocType), allocType)) + { + return true; + } + } + return false; +} + +void VmaBlockBufferImageGranularity::AllocPages(uint8_t allocType, VkDeviceSize offset, VkDeviceSize size) +{ + if (IsEnabled()) + { + uint32_t startPage = GetStartPage(offset); + AllocPage(m_RegionInfo[startPage], allocType); + + uint32_t endPage = GetEndPage(offset, size); + if (startPage != endPage) + AllocPage(m_RegionInfo[endPage], allocType); + } +} + +void VmaBlockBufferImageGranularity::FreePages(VkDeviceSize offset, VkDeviceSize size) +{ + if (IsEnabled()) + { + uint32_t startPage = GetStartPage(offset); + --m_RegionInfo[startPage].allocCount; + if (m_RegionInfo[startPage].allocCount == 0) + m_RegionInfo[startPage].allocType = VMA_SUBALLOCATION_TYPE_FREE; + uint32_t endPage = GetEndPage(offset, size); + if (startPage != endPage) + { + --m_RegionInfo[endPage].allocCount; + if (m_RegionInfo[endPage].allocCount == 0) + m_RegionInfo[endPage].allocType = VMA_SUBALLOCATION_TYPE_FREE; + } + } +} + +void VmaBlockBufferImageGranularity::Clear() +{ + if (m_RegionInfo) + memset(m_RegionInfo, 0, m_RegionCount * sizeof(RegionInfo)); +} + +VmaBlockBufferImageGranularity::ValidationContext VmaBlockBufferImageGranularity::StartValidation( + const VkAllocationCallbacks* pAllocationCallbacks, bool isVirutal) const +{ + ValidationContext ctx{ pAllocationCallbacks, VMA_NULL }; + if (!isVirutal && IsEnabled()) + { + ctx.pageAllocs = vma_new_array(pAllocationCallbacks, uint16_t, m_RegionCount); + memset(ctx.pageAllocs, 0, m_RegionCount * sizeof(uint16_t)); + } + return ctx; +} + +bool VmaBlockBufferImageGranularity::Validate(ValidationContext& ctx, + VkDeviceSize offset, VkDeviceSize size) const +{ + if (IsEnabled()) + { + uint32_t start = GetStartPage(offset); + ++ctx.pageAllocs[start]; + VMA_VALIDATE(m_RegionInfo[start].allocCount > 0); + + uint32_t end = GetEndPage(offset, size); + if (start != end) + { + ++ctx.pageAllocs[end]; + VMA_VALIDATE(m_RegionInfo[end].allocCount > 0); + } + } + return true; +} + +bool VmaBlockBufferImageGranularity::FinishValidation(ValidationContext& ctx) const +{ + // Check proper page structure + if (IsEnabled()) + { + VMA_ASSERT(ctx.pageAllocs != VMA_NULL && "Validation context not initialized!"); + + for (uint32_t page = 0; page < m_RegionCount; ++page) + { + VMA_VALIDATE(ctx.pageAllocs[page] == m_RegionInfo[page].allocCount); + } + vma_delete_array(ctx.allocCallbacks, ctx.pageAllocs, m_RegionCount); + ctx.pageAllocs = VMA_NULL; + } + return true; +} + +uint32_t VmaBlockBufferImageGranularity::OffsetToPageIndex(VkDeviceSize offset) const +{ + return static_cast(offset >> VMA_BITSCAN_MSB(m_BufferImageGranularity)); +} + +void VmaBlockBufferImageGranularity::AllocPage(RegionInfo& page, uint8_t allocType) +{ + // When current alloc type is free then it can be overridden by new type + if (page.allocCount == 0 || (page.allocCount > 0 && page.allocType == VMA_SUBALLOCATION_TYPE_FREE)) + page.allocType = allocType; + + ++page.allocCount; +} +#endif // _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY_FUNCTIONS +#endif // _VMA_BLOCK_BUFFER_IMAGE_GRANULARITY + +#if 0 +#ifndef _VMA_BLOCK_METADATA_GENERIC +class VmaBlockMetadata_Generic : public VmaBlockMetadata +{ + friend class VmaDefragmentationAlgorithm_Generic; + friend class VmaDefragmentationAlgorithm_Fast; + VMA_CLASS_NO_COPY(VmaBlockMetadata_Generic) +public: + VmaBlockMetadata_Generic(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual); + virtual ~VmaBlockMetadata_Generic() = default; + + size_t GetAllocationCount() const override { return m_Suballocations.size() - m_FreeCount; } + VkDeviceSize GetSumFreeSize() const override { return m_SumFreeSize; } + bool IsEmpty() const override { return (m_Suballocations.size() == 1) && (m_FreeCount == 1); } + void Free(VmaAllocHandle allocHandle) override { FreeSuballocation(FindAtOffset((VkDeviceSize)allocHandle - 1)); } + VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle - 1; } + + void Init(VkDeviceSize size) override; + bool Validate() const override; + + void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override; + void AddStatistics(VmaStatistics& inoutStats) const override; + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const override; +#endif + + bool CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) override; + + VkResult CheckCorruption(const void* pBlockData) override; + + void Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) override; + + void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override; + void* GetAllocationUserData(VmaAllocHandle allocHandle) const override; + VmaAllocHandle GetAllocationListBegin() const override; + VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override; + void Clear() override; + void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override; + void DebugLogAllAllocations() const override; + +private: + uint32_t m_FreeCount; + VkDeviceSize m_SumFreeSize; + VmaSuballocationList m_Suballocations; + // Suballocations that are free. Sorted by size, ascending. + VmaVector> m_FreeSuballocationsBySize; + + VkDeviceSize AlignAllocationSize(VkDeviceSize size) const { return IsVirtual() ? size : VmaAlignUp(size, (VkDeviceSize)16); } + + VmaSuballocationList::iterator FindAtOffset(VkDeviceSize offset) const; + bool ValidateFreeSuballocationList() const; + + // Checks if requested suballocation with given parameters can be placed in given pFreeSuballocItem. + // If yes, fills pOffset and returns true. If no, returns false. + bool CheckAllocation( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + VmaSuballocationList::const_iterator suballocItem, + VmaAllocHandle* pAllocHandle) const; + + // Given free suballocation, it merges it with following one, which must also be free. + void MergeFreeWithNext(VmaSuballocationList::iterator item); + // Releases given suballocation, making it free. + // Merges it with adjacent free suballocations if applicable. + // Returns iterator to new free suballocation at this place. + VmaSuballocationList::iterator FreeSuballocation(VmaSuballocationList::iterator suballocItem); + // Given free suballocation, it inserts it into sorted list of + // m_FreeSuballocationsBySize if it is suitable. + void RegisterFreeSuballocation(VmaSuballocationList::iterator item); + // Given free suballocation, it removes it from sorted list of + // m_FreeSuballocationsBySize if it is suitable. + void UnregisterFreeSuballocation(VmaSuballocationList::iterator item); +}; + +#ifndef _VMA_BLOCK_METADATA_GENERIC_FUNCTIONS +VmaBlockMetadata_Generic::VmaBlockMetadata_Generic(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual) + : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual), + m_FreeCount(0), + m_SumFreeSize(0), + m_Suballocations(VmaStlAllocator(pAllocationCallbacks)), + m_FreeSuballocationsBySize(VmaStlAllocator(pAllocationCallbacks)) {} + +void VmaBlockMetadata_Generic::Init(VkDeviceSize size) +{ + VmaBlockMetadata::Init(size); + + m_FreeCount = 1; + m_SumFreeSize = size; + + VmaSuballocation suballoc = {}; + suballoc.offset = 0; + suballoc.size = size; + suballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + + m_Suballocations.push_back(suballoc); + m_FreeSuballocationsBySize.push_back(m_Suballocations.begin()); +} + +bool VmaBlockMetadata_Generic::Validate() const +{ + VMA_VALIDATE(!m_Suballocations.empty()); + + // Expected offset of new suballocation as calculated from previous ones. + VkDeviceSize calculatedOffset = 0; + // Expected number of free suballocations as calculated from traversing their list. + uint32_t calculatedFreeCount = 0; + // Expected sum size of free suballocations as calculated from traversing their list. + VkDeviceSize calculatedSumFreeSize = 0; + // Expected number of free suballocations that should be registered in + // m_FreeSuballocationsBySize calculated from traversing their list. + size_t freeSuballocationsToRegister = 0; + // True if previous visited suballocation was free. + bool prevFree = false; + + const VkDeviceSize debugMargin = GetDebugMargin(); + + for (const auto& subAlloc : m_Suballocations) + { + // Actual offset of this suballocation doesn't match expected one. + VMA_VALIDATE(subAlloc.offset == calculatedOffset); + + const bool currFree = (subAlloc.type == VMA_SUBALLOCATION_TYPE_FREE); + // Two adjacent free suballocations are invalid. They should be merged. + VMA_VALIDATE(!prevFree || !currFree); + + VmaAllocation alloc = (VmaAllocation)subAlloc.userData; + if (!IsVirtual()) + { + VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE)); + } + + if (currFree) + { + calculatedSumFreeSize += subAlloc.size; + ++calculatedFreeCount; + ++freeSuballocationsToRegister; + + // Margin required between allocations - every free space must be at least that large. + VMA_VALIDATE(subAlloc.size >= debugMargin); + } + else + { + if (!IsVirtual()) + { + VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == subAlloc.offset + 1); + VMA_VALIDATE(alloc->GetSize() == subAlloc.size); + } + + // Margin required between allocations - previous allocation must be free. + VMA_VALIDATE(debugMargin == 0 || prevFree); + } + + calculatedOffset += subAlloc.size; + prevFree = currFree; + } + + // Number of free suballocations registered in m_FreeSuballocationsBySize doesn't + // match expected one. + VMA_VALIDATE(m_FreeSuballocationsBySize.size() == freeSuballocationsToRegister); + + VkDeviceSize lastSize = 0; + for (size_t i = 0; i < m_FreeSuballocationsBySize.size(); ++i) + { + VmaSuballocationList::iterator suballocItem = m_FreeSuballocationsBySize[i]; + + // Only free suballocations can be registered in m_FreeSuballocationsBySize. + VMA_VALIDATE(suballocItem->type == VMA_SUBALLOCATION_TYPE_FREE); + // They must be sorted by size ascending. + VMA_VALIDATE(suballocItem->size >= lastSize); + + lastSize = suballocItem->size; + } + + // Check if totals match calculated values. + VMA_VALIDATE(ValidateFreeSuballocationList()); + VMA_VALIDATE(calculatedOffset == GetSize()); + VMA_VALIDATE(calculatedSumFreeSize == m_SumFreeSize); + VMA_VALIDATE(calculatedFreeCount == m_FreeCount); + + return true; +} + +void VmaBlockMetadata_Generic::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const +{ + const uint32_t rangeCount = (uint32_t)m_Suballocations.size(); + inoutStats.statistics.blockCount++; + inoutStats.statistics.blockBytes += GetSize(); + + for (const auto& suballoc : m_Suballocations) + { + if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) + VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size); + else + VmaAddDetailedStatisticsUnusedRange(inoutStats, suballoc.size); + } +} + +void VmaBlockMetadata_Generic::AddStatistics(VmaStatistics& inoutStats) const +{ + inoutStats.blockCount++; + inoutStats.allocationCount += (uint32_t)m_Suballocations.size() - m_FreeCount; + inoutStats.blockBytes += GetSize(); + inoutStats.allocationBytes += GetSize() - m_SumFreeSize; +} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockMetadata_Generic::PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const +{ + PrintDetailedMap_Begin(json, + m_SumFreeSize, // unusedBytes + m_Suballocations.size() - (size_t)m_FreeCount, // allocationCount + m_FreeCount, // unusedRangeCount + mapRefCount); + + for (const auto& suballoc : m_Suballocations) + { + if (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE) + { + PrintDetailedMap_UnusedRange(json, suballoc.offset, suballoc.size); + } + else + { + PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData); + } + } + + PrintDetailedMap_End(json); +} +#endif // VMA_STATS_STRING_ENABLED + +bool VmaBlockMetadata_Generic::CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) +{ + VMA_ASSERT(allocSize > 0); + VMA_ASSERT(!upperAddress); + VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE); + VMA_ASSERT(pAllocationRequest != VMA_NULL); + VMA_HEAVY_ASSERT(Validate()); + + allocSize = AlignAllocationSize(allocSize); + + pAllocationRequest->type = VmaAllocationRequestType::Normal; + pAllocationRequest->size = allocSize; + + const VkDeviceSize debugMargin = GetDebugMargin(); + + // There is not enough total free space in this block to fulfill the request: Early return. + if (m_SumFreeSize < allocSize + debugMargin) + { + return false; + } + + // New algorithm, efficiently searching freeSuballocationsBySize. + const size_t freeSuballocCount = m_FreeSuballocationsBySize.size(); + if (freeSuballocCount > 0) + { + if (strategy == 0 || + strategy == VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT) + { + // Find first free suballocation with size not less than allocSize + debugMargin. + VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess( + m_FreeSuballocationsBySize.data(), + m_FreeSuballocationsBySize.data() + freeSuballocCount, + allocSize + debugMargin, + VmaSuballocationItemSizeLess()); + size_t index = it - m_FreeSuballocationsBySize.data(); + for (; index < freeSuballocCount; ++index) + { + if (CheckAllocation( + allocSize, + allocAlignment, + allocType, + m_FreeSuballocationsBySize[index], + &pAllocationRequest->allocHandle)) + { + pAllocationRequest->item = m_FreeSuballocationsBySize[index]; + return true; + } + } + } + else if (strategy == VMA_ALLOCATION_INTERNAL_STRATEGY_MIN_OFFSET) + { + for (VmaSuballocationList::iterator it = m_Suballocations.begin(); + it != m_Suballocations.end(); + ++it) + { + if (it->type == VMA_SUBALLOCATION_TYPE_FREE && CheckAllocation( + allocSize, + allocAlignment, + allocType, + it, + &pAllocationRequest->allocHandle)) + { + pAllocationRequest->item = it; + return true; + } + } + } + else + { + VMA_ASSERT(strategy & (VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT | VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT )); + // Search staring from biggest suballocations. + for (size_t index = freeSuballocCount; index--; ) + { + if (CheckAllocation( + allocSize, + allocAlignment, + allocType, + m_FreeSuballocationsBySize[index], + &pAllocationRequest->allocHandle)) + { + pAllocationRequest->item = m_FreeSuballocationsBySize[index]; + return true; + } + } + } + } + + return false; +} + +VkResult VmaBlockMetadata_Generic::CheckCorruption(const void* pBlockData) +{ + for (auto& suballoc : m_Suballocations) + { + if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) + { + if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!"); + return VK_ERROR_UNKNOWN_COPY; + } + } + } + + return VK_SUCCESS; +} + +void VmaBlockMetadata_Generic::Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) +{ + VMA_ASSERT(request.type == VmaAllocationRequestType::Normal); + VMA_ASSERT(request.item != m_Suballocations.end()); + VmaSuballocation& suballoc = *request.item; + // Given suballocation is a free block. + VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); + + // Given offset is inside this suballocation. + VMA_ASSERT((VkDeviceSize)request.allocHandle - 1 >= suballoc.offset); + const VkDeviceSize paddingBegin = (VkDeviceSize)request.allocHandle - suballoc.offset - 1; + VMA_ASSERT(suballoc.size >= paddingBegin + request.size); + const VkDeviceSize paddingEnd = suballoc.size - paddingBegin - request.size; + + // Unregister this free suballocation from m_FreeSuballocationsBySize and update + // it to become used. + UnregisterFreeSuballocation(request.item); + + suballoc.offset = (VkDeviceSize)request.allocHandle - 1; + suballoc.size = request.size; + suballoc.type = type; + suballoc.userData = userData; + + // If there are any free bytes remaining at the end, insert new free suballocation after current one. + if (paddingEnd) + { + VmaSuballocation paddingSuballoc = {}; + paddingSuballoc.offset = suballoc.offset + suballoc.size; + paddingSuballoc.size = paddingEnd; + paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + VmaSuballocationList::iterator next = request.item; + ++next; + const VmaSuballocationList::iterator paddingEndItem = + m_Suballocations.insert(next, paddingSuballoc); + RegisterFreeSuballocation(paddingEndItem); + } + + // If there are any free bytes remaining at the beginning, insert new free suballocation before current one. + if (paddingBegin) + { + VmaSuballocation paddingSuballoc = {}; + paddingSuballoc.offset = suballoc.offset - paddingBegin; + paddingSuballoc.size = paddingBegin; + paddingSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + const VmaSuballocationList::iterator paddingBeginItem = + m_Suballocations.insert(request.item, paddingSuballoc); + RegisterFreeSuballocation(paddingBeginItem); + } + + // Update totals. + m_FreeCount = m_FreeCount - 1; + if (paddingBegin > 0) + { + ++m_FreeCount; + } + if (paddingEnd > 0) + { + ++m_FreeCount; + } + m_SumFreeSize -= request.size; +} + +void VmaBlockMetadata_Generic::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) +{ + outInfo.offset = (VkDeviceSize)allocHandle - 1; + const VmaSuballocation& suballoc = *FindAtOffset(outInfo.offset); + outInfo.size = suballoc.size; + outInfo.pUserData = suballoc.userData; +} + +void* VmaBlockMetadata_Generic::GetAllocationUserData(VmaAllocHandle allocHandle) const +{ + return FindAtOffset((VkDeviceSize)allocHandle - 1)->userData; +} + +VmaAllocHandle VmaBlockMetadata_Generic::GetAllocationListBegin() const +{ + if (IsEmpty()) + return VK_NULL_HANDLE; + + for (const auto& suballoc : m_Suballocations) + { + if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) + return (VmaAllocHandle)(suballoc.offset + 1); + } + VMA_ASSERT(false && "Should contain at least 1 allocation!"); + return VK_NULL_HANDLE; +} + +VmaAllocHandle VmaBlockMetadata_Generic::GetNextAllocation(VmaAllocHandle prevAlloc) const +{ + VmaSuballocationList::const_iterator prev = FindAtOffset((VkDeviceSize)prevAlloc - 1); + + for (VmaSuballocationList::const_iterator it = ++prev; it != m_Suballocations.end(); ++it) + { + if (it->type != VMA_SUBALLOCATION_TYPE_FREE) + return (VmaAllocHandle)(it->offset + 1); + } + return VK_NULL_HANDLE; +} + +void VmaBlockMetadata_Generic::Clear() +{ + const VkDeviceSize size = GetSize(); + + VMA_ASSERT(IsVirtual()); + m_FreeCount = 1; + m_SumFreeSize = size; + m_Suballocations.clear(); + m_FreeSuballocationsBySize.clear(); + + VmaSuballocation suballoc = {}; + suballoc.offset = 0; + suballoc.size = size; + suballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + m_Suballocations.push_back(suballoc); + + m_FreeSuballocationsBySize.push_back(m_Suballocations.begin()); +} + +void VmaBlockMetadata_Generic::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) +{ + VmaSuballocation& suballoc = *FindAtOffset((VkDeviceSize)allocHandle - 1); + suballoc.userData = userData; +} + +void VmaBlockMetadata_Generic::DebugLogAllAllocations() const +{ + for (const auto& suballoc : m_Suballocations) + { + if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) + DebugLogAllocation(suballoc.offset, suballoc.size, suballoc.userData); + } +} + +VmaSuballocationList::iterator VmaBlockMetadata_Generic::FindAtOffset(VkDeviceSize offset) const +{ + VMA_HEAVY_ASSERT(!m_Suballocations.empty()); + const VkDeviceSize last = m_Suballocations.rbegin()->offset; + if (last == offset) + return m_Suballocations.rbegin().drop_const(); + const VkDeviceSize first = m_Suballocations.begin()->offset; + if (first == offset) + return m_Suballocations.begin().drop_const(); + + const size_t suballocCount = m_Suballocations.size(); + const VkDeviceSize step = (last - first + m_Suballocations.begin()->size) / suballocCount; + auto findSuballocation = [&](auto begin, auto end) -> VmaSuballocationList::iterator + { + for (auto suballocItem = begin; + suballocItem != end; + ++suballocItem) + { + if (suballocItem->offset == offset) + return suballocItem.drop_const(); + } + VMA_ASSERT(false && "Not found!"); + return m_Suballocations.end().drop_const(); + }; + // If requested offset is closer to the end of range, search from the end + if (offset - first > suballocCount * step / 2) + { + return findSuballocation(m_Suballocations.rbegin(), m_Suballocations.rend()); + } + return findSuballocation(m_Suballocations.begin(), m_Suballocations.end()); +} + +bool VmaBlockMetadata_Generic::ValidateFreeSuballocationList() const +{ + VkDeviceSize lastSize = 0; + for (size_t i = 0, count = m_FreeSuballocationsBySize.size(); i < count; ++i) + { + const VmaSuballocationList::iterator it = m_FreeSuballocationsBySize[i]; + + VMA_VALIDATE(it->type == VMA_SUBALLOCATION_TYPE_FREE); + VMA_VALIDATE(it->size >= lastSize); + lastSize = it->size; + } + return true; +} + +bool VmaBlockMetadata_Generic::CheckAllocation( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + VmaSuballocationList::const_iterator suballocItem, + VmaAllocHandle* pAllocHandle) const +{ + VMA_ASSERT(allocSize > 0); + VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE); + VMA_ASSERT(suballocItem != m_Suballocations.cend()); + VMA_ASSERT(pAllocHandle != VMA_NULL); + + const VkDeviceSize debugMargin = GetDebugMargin(); + const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity(); + + const VmaSuballocation& suballoc = *suballocItem; + VMA_ASSERT(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); + + // Size of this suballocation is too small for this request: Early return. + if (suballoc.size < allocSize) + { + return false; + } + + // Start from offset equal to beginning of this suballocation. + VkDeviceSize offset = suballoc.offset + (suballocItem == m_Suballocations.cbegin() ? 0 : GetDebugMargin()); + + // Apply debugMargin from the end of previous alloc. + if (debugMargin > 0) + { + offset += debugMargin; + } + + // Apply alignment. + offset = VmaAlignUp(offset, allocAlignment); + + // Check previous suballocations for BufferImageGranularity conflicts. + // Make bigger alignment if necessary. + if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment) + { + bool bufferImageGranularityConflict = false; + VmaSuballocationList::const_iterator prevSuballocItem = suballocItem; + while (prevSuballocItem != m_Suballocations.cbegin()) + { + --prevSuballocItem; + const VmaSuballocation& prevSuballoc = *prevSuballocItem; + if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, offset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType)) + { + bufferImageGranularityConflict = true; + break; + } + } + else + // Already on previous page. + break; + } + if (bufferImageGranularityConflict) + { + offset = VmaAlignUp(offset, bufferImageGranularity); + } + } + + // Calculate padding at the beginning based on current offset. + const VkDeviceSize paddingBegin = offset - suballoc.offset; + + // Fail if requested size plus margin after is bigger than size of this suballocation. + if (paddingBegin + allocSize + debugMargin > suballoc.size) + { + return false; + } + + // Check next suballocations for BufferImageGranularity conflicts. + // If conflict exists, allocation cannot be made here. + if (allocSize % bufferImageGranularity || offset % bufferImageGranularity) + { + VmaSuballocationList::const_iterator nextSuballocItem = suballocItem; + ++nextSuballocItem; + while (nextSuballocItem != m_Suballocations.cend()) + { + const VmaSuballocation& nextSuballoc = *nextSuballocItem; + if (VmaBlocksOnSamePage(offset, allocSize, nextSuballoc.offset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type)) + { + return false; + } + } + else + { + // Already on next page. + break; + } + ++nextSuballocItem; + } + } + + *pAllocHandle = (VmaAllocHandle)(offset + 1); + // All tests passed: Success. pAllocHandle is already filled. + return true; +} + +void VmaBlockMetadata_Generic::MergeFreeWithNext(VmaSuballocationList::iterator item) +{ + VMA_ASSERT(item != m_Suballocations.end()); + VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE); + + VmaSuballocationList::iterator nextItem = item; + ++nextItem; + VMA_ASSERT(nextItem != m_Suballocations.end()); + VMA_ASSERT(nextItem->type == VMA_SUBALLOCATION_TYPE_FREE); + + item->size += nextItem->size; + --m_FreeCount; + m_Suballocations.erase(nextItem); +} + +VmaSuballocationList::iterator VmaBlockMetadata_Generic::FreeSuballocation(VmaSuballocationList::iterator suballocItem) +{ + // Change this suballocation to be marked as free. + VmaSuballocation& suballoc = *suballocItem; + suballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + suballoc.userData = VMA_NULL; + + // Update totals. + ++m_FreeCount; + m_SumFreeSize += suballoc.size; + + // Merge with previous and/or next suballocation if it's also free. + bool mergeWithNext = false; + bool mergeWithPrev = false; + + VmaSuballocationList::iterator nextItem = suballocItem; + ++nextItem; + if ((nextItem != m_Suballocations.end()) && (nextItem->type == VMA_SUBALLOCATION_TYPE_FREE)) + { + mergeWithNext = true; + } + + VmaSuballocationList::iterator prevItem = suballocItem; + if (suballocItem != m_Suballocations.begin()) + { + --prevItem; + if (prevItem->type == VMA_SUBALLOCATION_TYPE_FREE) + { + mergeWithPrev = true; + } + } + + if (mergeWithNext) + { + UnregisterFreeSuballocation(nextItem); + MergeFreeWithNext(suballocItem); + } + + if (mergeWithPrev) + { + UnregisterFreeSuballocation(prevItem); + MergeFreeWithNext(prevItem); + RegisterFreeSuballocation(prevItem); + return prevItem; + } + else + { + RegisterFreeSuballocation(suballocItem); + return suballocItem; + } +} + +void VmaBlockMetadata_Generic::RegisterFreeSuballocation(VmaSuballocationList::iterator item) +{ + VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE); + VMA_ASSERT(item->size > 0); + + // You may want to enable this validation at the beginning or at the end of + // this function, depending on what do you want to check. + VMA_HEAVY_ASSERT(ValidateFreeSuballocationList()); + + if (m_FreeSuballocationsBySize.empty()) + { + m_FreeSuballocationsBySize.push_back(item); + } + else + { + VmaVectorInsertSorted(m_FreeSuballocationsBySize, item); + } + + //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList()); +} + +void VmaBlockMetadata_Generic::UnregisterFreeSuballocation(VmaSuballocationList::iterator item) +{ + VMA_ASSERT(item->type == VMA_SUBALLOCATION_TYPE_FREE); + VMA_ASSERT(item->size > 0); + + // You may want to enable this validation at the beginning or at the end of + // this function, depending on what do you want to check. + VMA_HEAVY_ASSERT(ValidateFreeSuballocationList()); + + VmaSuballocationList::iterator* const it = VmaBinaryFindFirstNotLess( + m_FreeSuballocationsBySize.data(), + m_FreeSuballocationsBySize.data() + m_FreeSuballocationsBySize.size(), + item, + VmaSuballocationItemSizeLess()); + for (size_t index = it - m_FreeSuballocationsBySize.data(); + index < m_FreeSuballocationsBySize.size(); + ++index) + { + if (m_FreeSuballocationsBySize[index] == item) + { + VmaVectorRemove(m_FreeSuballocationsBySize, index); + return; + } + VMA_ASSERT((m_FreeSuballocationsBySize[index]->size == item->size) && "Not found."); + } + VMA_ASSERT(0 && "Not found."); + + //VMA_HEAVY_ASSERT(ValidateFreeSuballocationList()); +} +#endif // _VMA_BLOCK_METADATA_GENERIC_FUNCTIONS +#endif // _VMA_BLOCK_METADATA_GENERIC +#endif // #if 0 + +#ifndef _VMA_BLOCK_METADATA_LINEAR +/* +Allocations and their references in internal data structure look like this: + +if(m_2ndVectorMode == SECOND_VECTOR_EMPTY): + + 0 +-------+ + | | + | | + | | + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount] + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount + 1] + +-------+ + | ... | + +-------+ + | Alloc | 1st[1st.size() - 1] + +-------+ + | | + | | + | | +GetSize() +-------+ + +if(m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER): + + 0 +-------+ + | Alloc | 2nd[0] + +-------+ + | Alloc | 2nd[1] + +-------+ + | ... | + +-------+ + | Alloc | 2nd[2nd.size() - 1] + +-------+ + | | + | | + | | + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount] + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount + 1] + +-------+ + | ... | + +-------+ + | Alloc | 1st[1st.size() - 1] + +-------+ + | | +GetSize() +-------+ + +if(m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK): + + 0 +-------+ + | | + | | + | | + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount] + +-------+ + | Alloc | 1st[m_1stNullItemsBeginCount + 1] + +-------+ + | ... | + +-------+ + | Alloc | 1st[1st.size() - 1] + +-------+ + | | + | | + | | + +-------+ + | Alloc | 2nd[2nd.size() - 1] + +-------+ + | ... | + +-------+ + | Alloc | 2nd[1] + +-------+ + | Alloc | 2nd[0] +GetSize() +-------+ + +*/ +class VmaBlockMetadata_Linear : public VmaBlockMetadata +{ + VMA_CLASS_NO_COPY(VmaBlockMetadata_Linear) +public: + VmaBlockMetadata_Linear(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual); + virtual ~VmaBlockMetadata_Linear() = default; + + VkDeviceSize GetSumFreeSize() const override { return m_SumFreeSize; } + bool IsEmpty() const override { return GetAllocationCount() == 0; } + VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle - 1; } + + void Init(VkDeviceSize size) override; + bool Validate() const override; + size_t GetAllocationCount() const override; + size_t GetFreeRegionsCount() const override; + + void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override; + void AddStatistics(VmaStatistics& inoutStats) const override; + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap(class VmaJsonWriter& json) const override; +#endif + + bool CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) override; + + VkResult CheckCorruption(const void* pBlockData) override; + + void Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) override; + + void Free(VmaAllocHandle allocHandle) override; + void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override; + void* GetAllocationUserData(VmaAllocHandle allocHandle) const override; + VmaAllocHandle GetAllocationListBegin() const override; + VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override; + VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const override; + void Clear() override; + void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override; + void DebugLogAllAllocations() const override; + +private: + /* + There are two suballocation vectors, used in ping-pong way. + The one with index m_1stVectorIndex is called 1st. + The one with index (m_1stVectorIndex ^ 1) is called 2nd. + 2nd can be non-empty only when 1st is not empty. + When 2nd is not empty, m_2ndVectorMode indicates its mode of operation. + */ + typedef VmaVector> SuballocationVectorType; + + enum SECOND_VECTOR_MODE + { + SECOND_VECTOR_EMPTY, + /* + Suballocations in 2nd vector are created later than the ones in 1st, but they + all have smaller offset. + */ + SECOND_VECTOR_RING_BUFFER, + /* + Suballocations in 2nd vector are upper side of double stack. + They all have offsets higher than those in 1st vector. + Top of this stack means smaller offsets, but higher indices in this vector. + */ + SECOND_VECTOR_DOUBLE_STACK, + }; + + VkDeviceSize m_SumFreeSize; + SuballocationVectorType m_Suballocations0, m_Suballocations1; + uint32_t m_1stVectorIndex; + SECOND_VECTOR_MODE m_2ndVectorMode; + // Number of items in 1st vector with hAllocation = null at the beginning. + size_t m_1stNullItemsBeginCount; + // Number of other items in 1st vector with hAllocation = null somewhere in the middle. + size_t m_1stNullItemsMiddleCount; + // Number of items in 2nd vector with hAllocation = null. + size_t m_2ndNullItemsCount; + + SuballocationVectorType& AccessSuballocations1st() { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; } + SuballocationVectorType& AccessSuballocations2nd() { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; } + const SuballocationVectorType& AccessSuballocations1st() const { return m_1stVectorIndex ? m_Suballocations1 : m_Suballocations0; } + const SuballocationVectorType& AccessSuballocations2nd() const { return m_1stVectorIndex ? m_Suballocations0 : m_Suballocations1; } + + VmaSuballocation& FindSuballocation(VkDeviceSize offset) const; + bool ShouldCompact1st() const; + void CleanupAfterFree(); + + bool CreateAllocationRequest_LowerAddress( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest); + bool CreateAllocationRequest_UpperAddress( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest); +}; + +#ifndef _VMA_BLOCK_METADATA_LINEAR_FUNCTIONS +VmaBlockMetadata_Linear::VmaBlockMetadata_Linear(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual) + : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual), + m_SumFreeSize(0), + m_Suballocations0(VmaStlAllocator(pAllocationCallbacks)), + m_Suballocations1(VmaStlAllocator(pAllocationCallbacks)), + m_1stVectorIndex(0), + m_2ndVectorMode(SECOND_VECTOR_EMPTY), + m_1stNullItemsBeginCount(0), + m_1stNullItemsMiddleCount(0), + m_2ndNullItemsCount(0) {} + +void VmaBlockMetadata_Linear::Init(VkDeviceSize size) +{ + VmaBlockMetadata::Init(size); + m_SumFreeSize = size; +} + +bool VmaBlockMetadata_Linear::Validate() const +{ + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + VMA_VALIDATE(suballocations2nd.empty() == (m_2ndVectorMode == SECOND_VECTOR_EMPTY)); + VMA_VALIDATE(!suballocations1st.empty() || + suballocations2nd.empty() || + m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER); + + if (!suballocations1st.empty()) + { + // Null item at the beginning should be accounted into m_1stNullItemsBeginCount. + VMA_VALIDATE(suballocations1st[m_1stNullItemsBeginCount].type != VMA_SUBALLOCATION_TYPE_FREE); + // Null item at the end should be just pop_back(). + VMA_VALIDATE(suballocations1st.back().type != VMA_SUBALLOCATION_TYPE_FREE); + } + if (!suballocations2nd.empty()) + { + // Null item at the end should be just pop_back(). + VMA_VALIDATE(suballocations2nd.back().type != VMA_SUBALLOCATION_TYPE_FREE); + } + + VMA_VALIDATE(m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount <= suballocations1st.size()); + VMA_VALIDATE(m_2ndNullItemsCount <= suballocations2nd.size()); + + VkDeviceSize sumUsedSize = 0; + const size_t suballoc1stCount = suballocations1st.size(); + const VkDeviceSize debugMargin = GetDebugMargin(); + VkDeviceSize offset = 0; + + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const size_t suballoc2ndCount = suballocations2nd.size(); + size_t nullItem2ndCount = 0; + for (size_t i = 0; i < suballoc2ndCount; ++i) + { + const VmaSuballocation& suballoc = suballocations2nd[i]; + const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); + + VmaAllocation const alloc = (VmaAllocation)suballoc.userData; + if (!IsVirtual()) + { + VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE)); + } + VMA_VALIDATE(suballoc.offset >= offset); + + if (!currFree) + { + if (!IsVirtual()) + { + VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1); + VMA_VALIDATE(alloc->GetSize() == suballoc.size); + } + sumUsedSize += suballoc.size; + } + else + { + ++nullItem2ndCount; + } + + offset = suballoc.offset + suballoc.size + debugMargin; + } + + VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount); + } + + for (size_t i = 0; i < m_1stNullItemsBeginCount; ++i) + { + const VmaSuballocation& suballoc = suballocations1st[i]; + VMA_VALIDATE(suballoc.type == VMA_SUBALLOCATION_TYPE_FREE && + suballoc.userData == VMA_NULL); + } + + size_t nullItem1stCount = m_1stNullItemsBeginCount; + + for (size_t i = m_1stNullItemsBeginCount; i < suballoc1stCount; ++i) + { + const VmaSuballocation& suballoc = suballocations1st[i]; + const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); + + VmaAllocation const alloc = (VmaAllocation)suballoc.userData; + if (!IsVirtual()) + { + VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE)); + } + VMA_VALIDATE(suballoc.offset >= offset); + VMA_VALIDATE(i >= m_1stNullItemsBeginCount || currFree); + + if (!currFree) + { + if (!IsVirtual()) + { + VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1); + VMA_VALIDATE(alloc->GetSize() == suballoc.size); + } + sumUsedSize += suballoc.size; + } + else + { + ++nullItem1stCount; + } + + offset = suballoc.offset + suballoc.size + debugMargin; + } + VMA_VALIDATE(nullItem1stCount == m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount); + + if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + const size_t suballoc2ndCount = suballocations2nd.size(); + size_t nullItem2ndCount = 0; + for (size_t i = suballoc2ndCount; i--; ) + { + const VmaSuballocation& suballoc = suballocations2nd[i]; + const bool currFree = (suballoc.type == VMA_SUBALLOCATION_TYPE_FREE); + + VmaAllocation const alloc = (VmaAllocation)suballoc.userData; + if (!IsVirtual()) + { + VMA_VALIDATE(currFree == (alloc == VK_NULL_HANDLE)); + } + VMA_VALIDATE(suballoc.offset >= offset); + + if (!currFree) + { + if (!IsVirtual()) + { + VMA_VALIDATE((VkDeviceSize)alloc->GetAllocHandle() == suballoc.offset + 1); + VMA_VALIDATE(alloc->GetSize() == suballoc.size); + } + sumUsedSize += suballoc.size; + } + else + { + ++nullItem2ndCount; + } + + offset = suballoc.offset + suballoc.size + debugMargin; + } + + VMA_VALIDATE(nullItem2ndCount == m_2ndNullItemsCount); + } + + VMA_VALIDATE(offset <= GetSize()); + VMA_VALIDATE(m_SumFreeSize == GetSize() - sumUsedSize); + + return true; +} + +size_t VmaBlockMetadata_Linear::GetAllocationCount() const +{ + return AccessSuballocations1st().size() - m_1stNullItemsBeginCount - m_1stNullItemsMiddleCount + + AccessSuballocations2nd().size() - m_2ndNullItemsCount; +} + +size_t VmaBlockMetadata_Linear::GetFreeRegionsCount() const +{ + // Function only used for defragmentation, which is disabled for this algorithm + VMA_ASSERT(0); + return SIZE_MAX; +} + +void VmaBlockMetadata_Linear::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const +{ + const VkDeviceSize size = GetSize(); + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + const size_t suballoc1stCount = suballocations1st.size(); + const size_t suballoc2ndCount = suballocations2nd.size(); + + inoutStats.statistics.blockCount++; + inoutStats.statistics.blockBytes += size; + + VkDeviceSize lastOffset = 0; + + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; + size_t nextAlloc2ndIndex = 0; + while (lastOffset < freeSpace2ndTo1stEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc2ndIndex < suballoc2ndCount && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + ++nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex < suballoc2ndCount) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc2ndIndex; + } + // We are at the end. + else + { + // There is free space from lastOffset to freeSpace2ndTo1stEnd. + if (lastOffset < freeSpace2ndTo1stEnd) + { + const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset; + VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace2ndTo1stEnd; + } + } + } + + size_t nextAlloc1stIndex = m_1stNullItemsBeginCount; + const VkDeviceSize freeSpace1stTo2ndEnd = + m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size; + while (lastOffset < freeSpace1stTo2ndEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc1stIndex < suballoc1stCount && + suballocations1st[nextAlloc1stIndex].userData == VMA_NULL) + { + ++nextAlloc1stIndex; + } + + // Found non-null allocation. + if (nextAlloc1stIndex < suballoc1stCount) + { + const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc1stIndex; + } + // We are at the end. + else + { + // There is free space from lastOffset to freeSpace1stTo2ndEnd. + if (lastOffset < freeSpace1stTo2ndEnd) + { + const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset; + VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace1stTo2ndEnd; + } + } + + if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; + while (lastOffset < size) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc2ndIndex != SIZE_MAX && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + --nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex != SIZE_MAX) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + VmaAddDetailedStatisticsAllocation(inoutStats, suballoc.size); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + --nextAlloc2ndIndex; + } + // We are at the end. + else + { + // There is free space from lastOffset to size. + if (lastOffset < size) + { + const VkDeviceSize unusedRangeSize = size - lastOffset; + VmaAddDetailedStatisticsUnusedRange(inoutStats, unusedRangeSize); + } + + // End of loop. + lastOffset = size; + } + } + } +} + +void VmaBlockMetadata_Linear::AddStatistics(VmaStatistics& inoutStats) const +{ + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + const VkDeviceSize size = GetSize(); + const size_t suballoc1stCount = suballocations1st.size(); + const size_t suballoc2ndCount = suballocations2nd.size(); + + inoutStats.blockCount++; + inoutStats.blockBytes += size; + inoutStats.allocationBytes += size - m_SumFreeSize; + + VkDeviceSize lastOffset = 0; + + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; + size_t nextAlloc2ndIndex = m_1stNullItemsBeginCount; + while (lastOffset < freeSpace2ndTo1stEnd) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex < suballoc2ndCount && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + ++nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex < suballoc2ndCount) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++inoutStats.allocationCount; + + // Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc2ndIndex; + } + // We are at the end. + else + { + // End of loop. + lastOffset = freeSpace2ndTo1stEnd; + } + } + } + + size_t nextAlloc1stIndex = m_1stNullItemsBeginCount; + const VkDeviceSize freeSpace1stTo2ndEnd = + m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size; + while (lastOffset < freeSpace1stTo2ndEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc1stIndex < suballoc1stCount && + suballocations1st[nextAlloc1stIndex].userData == VMA_NULL) + { + ++nextAlloc1stIndex; + } + + // Found non-null allocation. + if (nextAlloc1stIndex < suballoc1stCount) + { + const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex]; + + // Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++inoutStats.allocationCount; + + // Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc1stIndex; + } + // We are at the end. + else + { + // End of loop. + lastOffset = freeSpace1stTo2ndEnd; + } + } + + if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; + while (lastOffset < size) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex != SIZE_MAX && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + --nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex != SIZE_MAX) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++inoutStats.allocationCount; + + // Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + --nextAlloc2ndIndex; + } + // We are at the end. + else + { + // End of loop. + lastOffset = size; + } + } + } +} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockMetadata_Linear::PrintDetailedMap(class VmaJsonWriter& json) const +{ + const VkDeviceSize size = GetSize(); + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + const size_t suballoc1stCount = suballocations1st.size(); + const size_t suballoc2ndCount = suballocations2nd.size(); + + // FIRST PASS + + size_t unusedRangeCount = 0; + VkDeviceSize usedBytes = 0; + + VkDeviceSize lastOffset = 0; + + size_t alloc2ndCount = 0; + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; + size_t nextAlloc2ndIndex = 0; + while (lastOffset < freeSpace2ndTo1stEnd) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex < suballoc2ndCount && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + ++nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex < suballoc2ndCount) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + ++unusedRangeCount; + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++alloc2ndCount; + usedBytes += suballoc.size; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc2ndIndex; + } + // We are at the end. + else + { + if (lastOffset < freeSpace2ndTo1stEnd) + { + // There is free space from lastOffset to freeSpace2ndTo1stEnd. + ++unusedRangeCount; + } + + // End of loop. + lastOffset = freeSpace2ndTo1stEnd; + } + } + } + + size_t nextAlloc1stIndex = m_1stNullItemsBeginCount; + size_t alloc1stCount = 0; + const VkDeviceSize freeSpace1stTo2ndEnd = + m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? suballocations2nd.back().offset : size; + while (lastOffset < freeSpace1stTo2ndEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc1stIndex < suballoc1stCount && + suballocations1st[nextAlloc1stIndex].userData == VMA_NULL) + { + ++nextAlloc1stIndex; + } + + // Found non-null allocation. + if (nextAlloc1stIndex < suballoc1stCount) + { + const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + ++unusedRangeCount; + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++alloc1stCount; + usedBytes += suballoc.size; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc1stIndex; + } + // We are at the end. + else + { + if (lastOffset < size) + { + // There is free space from lastOffset to freeSpace1stTo2ndEnd. + ++unusedRangeCount; + } + + // End of loop. + lastOffset = freeSpace1stTo2ndEnd; + } + } + + if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; + while (lastOffset < size) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex != SIZE_MAX && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + --nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex != SIZE_MAX) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + ++unusedRangeCount; + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + ++alloc2ndCount; + usedBytes += suballoc.size; + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + --nextAlloc2ndIndex; + } + // We are at the end. + else + { + if (lastOffset < size) + { + // There is free space from lastOffset to size. + ++unusedRangeCount; + } + + // End of loop. + lastOffset = size; + } + } + } + + const VkDeviceSize unusedBytes = size - usedBytes; + PrintDetailedMap_Begin(json, unusedBytes, alloc1stCount + alloc2ndCount, unusedRangeCount); + + // SECOND PASS + lastOffset = 0; + + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + const VkDeviceSize freeSpace2ndTo1stEnd = suballocations1st[m_1stNullItemsBeginCount].offset; + size_t nextAlloc2ndIndex = 0; + while (lastOffset < freeSpace2ndTo1stEnd) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex < suballoc2ndCount && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + ++nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex < suballoc2ndCount) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc2ndIndex; + } + // We are at the end. + else + { + if (lastOffset < freeSpace2ndTo1stEnd) + { + // There is free space from lastOffset to freeSpace2ndTo1stEnd. + const VkDeviceSize unusedRangeSize = freeSpace2ndTo1stEnd - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace2ndTo1stEnd; + } + } + } + + nextAlloc1stIndex = m_1stNullItemsBeginCount; + while (lastOffset < freeSpace1stTo2ndEnd) + { + // Find next non-null allocation or move nextAllocIndex to the end. + while (nextAlloc1stIndex < suballoc1stCount && + suballocations1st[nextAlloc1stIndex].userData == VMA_NULL) + { + ++nextAlloc1stIndex; + } + + // Found non-null allocation. + if (nextAlloc1stIndex < suballoc1stCount) + { + const VmaSuballocation& suballoc = suballocations1st[nextAlloc1stIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + ++nextAlloc1stIndex; + } + // We are at the end. + else + { + if (lastOffset < freeSpace1stTo2ndEnd) + { + // There is free space from lastOffset to freeSpace1stTo2ndEnd. + const VkDeviceSize unusedRangeSize = freeSpace1stTo2ndEnd - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // End of loop. + lastOffset = freeSpace1stTo2ndEnd; + } + } + + if (m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + size_t nextAlloc2ndIndex = suballocations2nd.size() - 1; + while (lastOffset < size) + { + // Find next non-null allocation or move nextAlloc2ndIndex to the end. + while (nextAlloc2ndIndex != SIZE_MAX && + suballocations2nd[nextAlloc2ndIndex].userData == VMA_NULL) + { + --nextAlloc2ndIndex; + } + + // Found non-null allocation. + if (nextAlloc2ndIndex != SIZE_MAX) + { + const VmaSuballocation& suballoc = suballocations2nd[nextAlloc2ndIndex]; + + // 1. Process free space before this allocation. + if (lastOffset < suballoc.offset) + { + // There is free space from lastOffset to suballoc.offset. + const VkDeviceSize unusedRangeSize = suballoc.offset - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // 2. Process this allocation. + // There is allocation with suballoc.offset, suballoc.size. + PrintDetailedMap_Allocation(json, suballoc.offset, suballoc.size, suballoc.userData); + + // 3. Prepare for next iteration. + lastOffset = suballoc.offset + suballoc.size; + --nextAlloc2ndIndex; + } + // We are at the end. + else + { + if (lastOffset < size) + { + // There is free space from lastOffset to size. + const VkDeviceSize unusedRangeSize = size - lastOffset; + PrintDetailedMap_UnusedRange(json, lastOffset, unusedRangeSize); + } + + // End of loop. + lastOffset = size; + } + } + } + + PrintDetailedMap_End(json); +} +#endif // VMA_STATS_STRING_ENABLED + +bool VmaBlockMetadata_Linear::CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) +{ + VMA_ASSERT(allocSize > 0); + VMA_ASSERT(allocType != VMA_SUBALLOCATION_TYPE_FREE); + VMA_ASSERT(pAllocationRequest != VMA_NULL); + VMA_HEAVY_ASSERT(Validate()); + pAllocationRequest->size = allocSize; + return upperAddress ? + CreateAllocationRequest_UpperAddress( + allocSize, allocAlignment, allocType, strategy, pAllocationRequest) : + CreateAllocationRequest_LowerAddress( + allocSize, allocAlignment, allocType, strategy, pAllocationRequest); +} + +VkResult VmaBlockMetadata_Linear::CheckCorruption(const void* pBlockData) +{ + VMA_ASSERT(!IsVirtual()); + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + for (size_t i = m_1stNullItemsBeginCount, count = suballocations1st.size(); i < count; ++i) + { + const VmaSuballocation& suballoc = suballocations1st[i]; + if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) + { + if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!"); + return VK_ERROR_UNKNOWN_COPY; + } + } + } + + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + for (size_t i = 0, count = suballocations2nd.size(); i < count; ++i) + { + const VmaSuballocation& suballoc = suballocations2nd[i]; + if (suballoc.type != VMA_SUBALLOCATION_TYPE_FREE) + { + if (!VmaValidateMagicValue(pBlockData, suballoc.offset + suballoc.size)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!"); + return VK_ERROR_UNKNOWN_COPY; + } + } + } + + return VK_SUCCESS; +} + +void VmaBlockMetadata_Linear::Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) +{ + const VkDeviceSize offset = (VkDeviceSize)request.allocHandle - 1; + const VmaSuballocation newSuballoc = { offset, request.size, userData, type }; + + switch (request.type) + { + case VmaAllocationRequestType::UpperAddress: + { + VMA_ASSERT(m_2ndVectorMode != SECOND_VECTOR_RING_BUFFER && + "CRITICAL ERROR: Trying to use linear allocator as double stack while it was already used as ring buffer."); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + suballocations2nd.push_back(newSuballoc); + m_2ndVectorMode = SECOND_VECTOR_DOUBLE_STACK; + } + break; + case VmaAllocationRequestType::EndOf1st: + { + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + + VMA_ASSERT(suballocations1st.empty() || + offset >= suballocations1st.back().offset + suballocations1st.back().size); + // Check if it fits before the end of the block. + VMA_ASSERT(offset + request.size <= GetSize()); + + suballocations1st.push_back(newSuballoc); + } + break; + case VmaAllocationRequestType::EndOf2nd: + { + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + // New allocation at the end of 2-part ring buffer, so before first allocation from 1st vector. + VMA_ASSERT(!suballocations1st.empty() && + offset + request.size <= suballocations1st[m_1stNullItemsBeginCount].offset); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + switch (m_2ndVectorMode) + { + case SECOND_VECTOR_EMPTY: + // First allocation from second part ring buffer. + VMA_ASSERT(suballocations2nd.empty()); + m_2ndVectorMode = SECOND_VECTOR_RING_BUFFER; + break; + case SECOND_VECTOR_RING_BUFFER: + // 2-part ring buffer is already started. + VMA_ASSERT(!suballocations2nd.empty()); + break; + case SECOND_VECTOR_DOUBLE_STACK: + VMA_ASSERT(0 && "CRITICAL ERROR: Trying to use linear allocator as ring buffer while it was already used as double stack."); + break; + default: + VMA_ASSERT(0); + } + + suballocations2nd.push_back(newSuballoc); + } + break; + default: + VMA_ASSERT(0 && "CRITICAL INTERNAL ERROR."); + } + + m_SumFreeSize -= newSuballoc.size; +} + +void VmaBlockMetadata_Linear::Free(VmaAllocHandle allocHandle) +{ + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + VkDeviceSize offset = (VkDeviceSize)allocHandle - 1; + + if (!suballocations1st.empty()) + { + // First allocation: Mark it as next empty at the beginning. + VmaSuballocation& firstSuballoc = suballocations1st[m_1stNullItemsBeginCount]; + if (firstSuballoc.offset == offset) + { + firstSuballoc.type = VMA_SUBALLOCATION_TYPE_FREE; + firstSuballoc.userData = VMA_NULL; + m_SumFreeSize += firstSuballoc.size; + ++m_1stNullItemsBeginCount; + CleanupAfterFree(); + return; + } + } + + // Last allocation in 2-part ring buffer or top of upper stack (same logic). + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER || + m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + VmaSuballocation& lastSuballoc = suballocations2nd.back(); + if (lastSuballoc.offset == offset) + { + m_SumFreeSize += lastSuballoc.size; + suballocations2nd.pop_back(); + CleanupAfterFree(); + return; + } + } + // Last allocation in 1st vector. + else if (m_2ndVectorMode == SECOND_VECTOR_EMPTY) + { + VmaSuballocation& lastSuballoc = suballocations1st.back(); + if (lastSuballoc.offset == offset) + { + m_SumFreeSize += lastSuballoc.size; + suballocations1st.pop_back(); + CleanupAfterFree(); + return; + } + } + + VmaSuballocation refSuballoc; + refSuballoc.offset = offset; + // Rest of members stays uninitialized intentionally for better performance. + + // Item from the middle of 1st vector. + { + const SuballocationVectorType::iterator it = VmaBinaryFindSorted( + suballocations1st.begin() + m_1stNullItemsBeginCount, + suballocations1st.end(), + refSuballoc, + VmaSuballocationOffsetLess()); + if (it != suballocations1st.end()) + { + it->type = VMA_SUBALLOCATION_TYPE_FREE; + it->userData = VMA_NULL; + ++m_1stNullItemsMiddleCount; + m_SumFreeSize += it->size; + CleanupAfterFree(); + return; + } + } + + if (m_2ndVectorMode != SECOND_VECTOR_EMPTY) + { + // Item from the middle of 2nd vector. + const SuballocationVectorType::iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ? + VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) : + VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater()); + if (it != suballocations2nd.end()) + { + it->type = VMA_SUBALLOCATION_TYPE_FREE; + it->userData = VMA_NULL; + ++m_2ndNullItemsCount; + m_SumFreeSize += it->size; + CleanupAfterFree(); + return; + } + } + + VMA_ASSERT(0 && "Allocation to free not found in linear allocator!"); +} + +void VmaBlockMetadata_Linear::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) +{ + outInfo.offset = (VkDeviceSize)allocHandle - 1; + VmaSuballocation& suballoc = FindSuballocation(outInfo.offset); + outInfo.size = suballoc.size; + outInfo.pUserData = suballoc.userData; +} + +void* VmaBlockMetadata_Linear::GetAllocationUserData(VmaAllocHandle allocHandle) const +{ + return FindSuballocation((VkDeviceSize)allocHandle - 1).userData; +} + +VmaAllocHandle VmaBlockMetadata_Linear::GetAllocationListBegin() const +{ + // Function only used for defragmentation, which is disabled for this algorithm + VMA_ASSERT(0); + return VK_NULL_HANDLE; +} + +VmaAllocHandle VmaBlockMetadata_Linear::GetNextAllocation(VmaAllocHandle prevAlloc) const +{ + // Function only used for defragmentation, which is disabled for this algorithm + VMA_ASSERT(0); + return VK_NULL_HANDLE; +} + +VkDeviceSize VmaBlockMetadata_Linear::GetNextFreeRegionSize(VmaAllocHandle alloc) const +{ + // Function only used for defragmentation, which is disabled for this algorithm + VMA_ASSERT(0); + return 0; +} + +void VmaBlockMetadata_Linear::Clear() +{ + m_SumFreeSize = GetSize(); + m_Suballocations0.clear(); + m_Suballocations1.clear(); + // Leaving m_1stVectorIndex unchanged - it doesn't matter. + m_2ndVectorMode = SECOND_VECTOR_EMPTY; + m_1stNullItemsBeginCount = 0; + m_1stNullItemsMiddleCount = 0; + m_2ndNullItemsCount = 0; +} + +void VmaBlockMetadata_Linear::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) +{ + VmaSuballocation& suballoc = FindSuballocation((VkDeviceSize)allocHandle - 1); + suballoc.userData = userData; +} + +void VmaBlockMetadata_Linear::DebugLogAllAllocations() const +{ + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + for (auto it = suballocations1st.begin() + m_1stNullItemsBeginCount; it != suballocations1st.end(); ++it) + if (it->type != VMA_SUBALLOCATION_TYPE_FREE) + DebugLogAllocation(it->offset, it->size, it->userData); + + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + for (auto it = suballocations2nd.begin(); it != suballocations2nd.end(); ++it) + if (it->type != VMA_SUBALLOCATION_TYPE_FREE) + DebugLogAllocation(it->offset, it->size, it->userData); +} + +VmaSuballocation& VmaBlockMetadata_Linear::FindSuballocation(VkDeviceSize offset) const +{ + const SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + const SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + VmaSuballocation refSuballoc; + refSuballoc.offset = offset; + // Rest of members stays uninitialized intentionally for better performance. + + // Item from the 1st vector. + { + SuballocationVectorType::const_iterator it = VmaBinaryFindSorted( + suballocations1st.begin() + m_1stNullItemsBeginCount, + suballocations1st.end(), + refSuballoc, + VmaSuballocationOffsetLess()); + if (it != suballocations1st.end()) + { + return const_cast(*it); + } + } + + if (m_2ndVectorMode != SECOND_VECTOR_EMPTY) + { + // Rest of members stays uninitialized intentionally for better performance. + SuballocationVectorType::const_iterator it = m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER ? + VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetLess()) : + VmaBinaryFindSorted(suballocations2nd.begin(), suballocations2nd.end(), refSuballoc, VmaSuballocationOffsetGreater()); + if (it != suballocations2nd.end()) + { + return const_cast(*it); + } + } + + VMA_ASSERT(0 && "Allocation not found in linear allocator!"); + return const_cast(suballocations1st.back()); // Should never occur. +} + +bool VmaBlockMetadata_Linear::ShouldCompact1st() const +{ + const size_t nullItemCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount; + const size_t suballocCount = AccessSuballocations1st().size(); + return suballocCount > 32 && nullItemCount * 2 >= (suballocCount - nullItemCount) * 3; +} + +void VmaBlockMetadata_Linear::CleanupAfterFree() +{ + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + if (IsEmpty()) + { + suballocations1st.clear(); + suballocations2nd.clear(); + m_1stNullItemsBeginCount = 0; + m_1stNullItemsMiddleCount = 0; + m_2ndNullItemsCount = 0; + m_2ndVectorMode = SECOND_VECTOR_EMPTY; + } + else + { + const size_t suballoc1stCount = suballocations1st.size(); + const size_t nullItem1stCount = m_1stNullItemsBeginCount + m_1stNullItemsMiddleCount; + VMA_ASSERT(nullItem1stCount <= suballoc1stCount); + + // Find more null items at the beginning of 1st vector. + while (m_1stNullItemsBeginCount < suballoc1stCount && + suballocations1st[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE) + { + ++m_1stNullItemsBeginCount; + --m_1stNullItemsMiddleCount; + } + + // Find more null items at the end of 1st vector. + while (m_1stNullItemsMiddleCount > 0 && + suballocations1st.back().type == VMA_SUBALLOCATION_TYPE_FREE) + { + --m_1stNullItemsMiddleCount; + suballocations1st.pop_back(); + } + + // Find more null items at the end of 2nd vector. + while (m_2ndNullItemsCount > 0 && + suballocations2nd.back().type == VMA_SUBALLOCATION_TYPE_FREE) + { + --m_2ndNullItemsCount; + suballocations2nd.pop_back(); + } + + // Find more null items at the beginning of 2nd vector. + while (m_2ndNullItemsCount > 0 && + suballocations2nd[0].type == VMA_SUBALLOCATION_TYPE_FREE) + { + --m_2ndNullItemsCount; + VmaVectorRemove(suballocations2nd, 0); + } + + if (ShouldCompact1st()) + { + const size_t nonNullItemCount = suballoc1stCount - nullItem1stCount; + size_t srcIndex = m_1stNullItemsBeginCount; + for (size_t dstIndex = 0; dstIndex < nonNullItemCount; ++dstIndex) + { + while (suballocations1st[srcIndex].type == VMA_SUBALLOCATION_TYPE_FREE) + { + ++srcIndex; + } + if (dstIndex != srcIndex) + { + suballocations1st[dstIndex] = suballocations1st[srcIndex]; + } + ++srcIndex; + } + suballocations1st.resize(nonNullItemCount); + m_1stNullItemsBeginCount = 0; + m_1stNullItemsMiddleCount = 0; + } + + // 2nd vector became empty. + if (suballocations2nd.empty()) + { + m_2ndVectorMode = SECOND_VECTOR_EMPTY; + } + + // 1st vector became empty. + if (suballocations1st.size() - m_1stNullItemsBeginCount == 0) + { + suballocations1st.clear(); + m_1stNullItemsBeginCount = 0; + + if (!suballocations2nd.empty() && m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + // Swap 1st with 2nd. Now 2nd is empty. + m_2ndVectorMode = SECOND_VECTOR_EMPTY; + m_1stNullItemsMiddleCount = m_2ndNullItemsCount; + while (m_1stNullItemsBeginCount < suballocations2nd.size() && + suballocations2nd[m_1stNullItemsBeginCount].type == VMA_SUBALLOCATION_TYPE_FREE) + { + ++m_1stNullItemsBeginCount; + --m_1stNullItemsMiddleCount; + } + m_2ndNullItemsCount = 0; + m_1stVectorIndex ^= 1; + } + } + } + + VMA_HEAVY_ASSERT(Validate()); +} + +bool VmaBlockMetadata_Linear::CreateAllocationRequest_LowerAddress( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) +{ + const VkDeviceSize blockSize = GetSize(); + const VkDeviceSize debugMargin = GetDebugMargin(); + const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity(); + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + // Try to allocate at the end of 1st vector. + + VkDeviceSize resultBaseOffset = 0; + if (!suballocations1st.empty()) + { + const VmaSuballocation& lastSuballoc = suballocations1st.back(); + resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + debugMargin; + } + + // Start from offset equal to beginning of free space. + VkDeviceSize resultOffset = resultBaseOffset; + + // Apply alignment. + resultOffset = VmaAlignUp(resultOffset, allocAlignment); + + // Check previous suballocations for BufferImageGranularity conflicts. + // Make bigger alignment if necessary. + if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations1st.empty()) + { + bool bufferImageGranularityConflict = false; + for (size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; ) + { + const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex]; + if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType)) + { + bufferImageGranularityConflict = true; + break; + } + } + else + // Already on previous page. + break; + } + if (bufferImageGranularityConflict) + { + resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity); + } + } + + const VkDeviceSize freeSpaceEnd = m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK ? + suballocations2nd.back().offset : blockSize; + + // There is enough free space at the end after alignment. + if (resultOffset + allocSize + debugMargin <= freeSpaceEnd) + { + // Check next suballocations for BufferImageGranularity conflicts. + // If conflict exists, allocation cannot be made here. + if ((allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity) && m_2ndVectorMode == SECOND_VECTOR_DOUBLE_STACK) + { + for (size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; ) + { + const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex]; + if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type)) + { + return false; + } + } + else + { + // Already on previous page. + break; + } + } + } + + // All tests passed: Success. + pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1); + // pAllocationRequest->item, customData unused. + pAllocationRequest->type = VmaAllocationRequestType::EndOf1st; + return true; + } + } + + // Wrap-around to end of 2nd vector. Try to allocate there, watching for the + // beginning of 1st vector as the end of free space. + if (m_2ndVectorMode == SECOND_VECTOR_EMPTY || m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + VMA_ASSERT(!suballocations1st.empty()); + + VkDeviceSize resultBaseOffset = 0; + if (!suballocations2nd.empty()) + { + const VmaSuballocation& lastSuballoc = suballocations2nd.back(); + resultBaseOffset = lastSuballoc.offset + lastSuballoc.size + debugMargin; + } + + // Start from offset equal to beginning of free space. + VkDeviceSize resultOffset = resultBaseOffset; + + // Apply alignment. + resultOffset = VmaAlignUp(resultOffset, allocAlignment); + + // Check previous suballocations for BufferImageGranularity conflicts. + // Make bigger alignment if necessary. + if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty()) + { + bool bufferImageGranularityConflict = false; + for (size_t prevSuballocIndex = suballocations2nd.size(); prevSuballocIndex--; ) + { + const VmaSuballocation& prevSuballoc = suballocations2nd[prevSuballocIndex]; + if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(prevSuballoc.type, allocType)) + { + bufferImageGranularityConflict = true; + break; + } + } + else + // Already on previous page. + break; + } + if (bufferImageGranularityConflict) + { + resultOffset = VmaAlignUp(resultOffset, bufferImageGranularity); + } + } + + size_t index1st = m_1stNullItemsBeginCount; + + // There is enough free space at the end after alignment. + if ((index1st == suballocations1st.size() && resultOffset + allocSize + debugMargin <= blockSize) || + (index1st < suballocations1st.size() && resultOffset + allocSize + debugMargin <= suballocations1st[index1st].offset)) + { + // Check next suballocations for BufferImageGranularity conflicts. + // If conflict exists, allocation cannot be made here. + if (allocSize % bufferImageGranularity || resultOffset % bufferImageGranularity) + { + for (size_t nextSuballocIndex = index1st; + nextSuballocIndex < suballocations1st.size(); + nextSuballocIndex++) + { + const VmaSuballocation& nextSuballoc = suballocations1st[nextSuballocIndex]; + if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(allocType, nextSuballoc.type)) + { + return false; + } + } + else + { + // Already on next page. + break; + } + } + } + + // All tests passed: Success. + pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1); + pAllocationRequest->type = VmaAllocationRequestType::EndOf2nd; + // pAllocationRequest->item, customData unused. + return true; + } + } + + return false; +} + +bool VmaBlockMetadata_Linear::CreateAllocationRequest_UpperAddress( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) +{ + const VkDeviceSize blockSize = GetSize(); + const VkDeviceSize bufferImageGranularity = GetBufferImageGranularity(); + SuballocationVectorType& suballocations1st = AccessSuballocations1st(); + SuballocationVectorType& suballocations2nd = AccessSuballocations2nd(); + + if (m_2ndVectorMode == SECOND_VECTOR_RING_BUFFER) + { + VMA_ASSERT(0 && "Trying to use pool with linear algorithm as double stack, while it is already being used as ring buffer."); + return false; + } + + // Try to allocate before 2nd.back(), or end of block if 2nd.empty(). + if (allocSize > blockSize) + { + return false; + } + VkDeviceSize resultBaseOffset = blockSize - allocSize; + if (!suballocations2nd.empty()) + { + const VmaSuballocation& lastSuballoc = suballocations2nd.back(); + resultBaseOffset = lastSuballoc.offset - allocSize; + if (allocSize > lastSuballoc.offset) + { + return false; + } + } + + // Start from offset equal to end of free space. + VkDeviceSize resultOffset = resultBaseOffset; + + const VkDeviceSize debugMargin = GetDebugMargin(); + + // Apply debugMargin at the end. + if (debugMargin > 0) + { + if (resultOffset < debugMargin) + { + return false; + } + resultOffset -= debugMargin; + } + + // Apply alignment. + resultOffset = VmaAlignDown(resultOffset, allocAlignment); + + // Check next suballocations from 2nd for BufferImageGranularity conflicts. + // Make bigger alignment if necessary. + if (bufferImageGranularity > 1 && bufferImageGranularity != allocAlignment && !suballocations2nd.empty()) + { + bool bufferImageGranularityConflict = false; + for (size_t nextSuballocIndex = suballocations2nd.size(); nextSuballocIndex--; ) + { + const VmaSuballocation& nextSuballoc = suballocations2nd[nextSuballocIndex]; + if (VmaBlocksOnSamePage(resultOffset, allocSize, nextSuballoc.offset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(nextSuballoc.type, allocType)) + { + bufferImageGranularityConflict = true; + break; + } + } + else + // Already on previous page. + break; + } + if (bufferImageGranularityConflict) + { + resultOffset = VmaAlignDown(resultOffset, bufferImageGranularity); + } + } + + // There is enough free space. + const VkDeviceSize endOf1st = !suballocations1st.empty() ? + suballocations1st.back().offset + suballocations1st.back().size : + 0; + if (endOf1st + debugMargin <= resultOffset) + { + // Check previous suballocations for BufferImageGranularity conflicts. + // If conflict exists, allocation cannot be made here. + if (bufferImageGranularity > 1) + { + for (size_t prevSuballocIndex = suballocations1st.size(); prevSuballocIndex--; ) + { + const VmaSuballocation& prevSuballoc = suballocations1st[prevSuballocIndex]; + if (VmaBlocksOnSamePage(prevSuballoc.offset, prevSuballoc.size, resultOffset, bufferImageGranularity)) + { + if (VmaIsBufferImageGranularityConflict(allocType, prevSuballoc.type)) + { + return false; + } + } + else + { + // Already on next page. + break; + } + } + } + + // All tests passed: Success. + pAllocationRequest->allocHandle = (VmaAllocHandle)(resultOffset + 1); + // pAllocationRequest->item unused. + pAllocationRequest->type = VmaAllocationRequestType::UpperAddress; + return true; + } + + return false; +} +#endif // _VMA_BLOCK_METADATA_LINEAR_FUNCTIONS +#endif // _VMA_BLOCK_METADATA_LINEAR + +#if 0 +#ifndef _VMA_BLOCK_METADATA_BUDDY +/* +- GetSize() is the original size of allocated memory block. +- m_UsableSize is this size aligned down to a power of two. + All allocations and calculations happen relative to m_UsableSize. +- GetUnusableSize() is the difference between them. + It is reported as separate, unused range, not available for allocations. + +Node at level 0 has size = m_UsableSize. +Each next level contains nodes with size 2 times smaller than current level. +m_LevelCount is the maximum number of levels to use in the current object. +*/ +class VmaBlockMetadata_Buddy : public VmaBlockMetadata +{ + VMA_CLASS_NO_COPY(VmaBlockMetadata_Buddy) +public: + VmaBlockMetadata_Buddy(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual); + virtual ~VmaBlockMetadata_Buddy(); + + size_t GetAllocationCount() const override { return m_AllocationCount; } + VkDeviceSize GetSumFreeSize() const override { return m_SumFreeSize + GetUnusableSize(); } + bool IsEmpty() const override { return m_Root->type == Node::TYPE_FREE; } + VkResult CheckCorruption(const void* pBlockData) override { return VK_ERROR_FEATURE_NOT_PRESENT; } + VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return (VkDeviceSize)allocHandle - 1; } + void DebugLogAllAllocations() const override { DebugLogAllAllocationNode(m_Root, 0); } + + void Init(VkDeviceSize size) override; + bool Validate() const override; + + void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override; + void AddStatistics(VmaStatistics& inoutStats) const override; + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const override; +#endif + + bool CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) override; + + void Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) override; + + void Free(VmaAllocHandle allocHandle) override; + void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override; + void* GetAllocationUserData(VmaAllocHandle allocHandle) const override; + VmaAllocHandle GetAllocationListBegin() const override; + VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override; + void Clear() override; + void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override; + +private: + static const size_t MAX_LEVELS = 48; + + struct ValidationContext + { + size_t calculatedAllocationCount = 0; + size_t calculatedFreeCount = 0; + VkDeviceSize calculatedSumFreeSize = 0; + }; + struct Node + { + VkDeviceSize offset; + enum TYPE + { + TYPE_FREE, + TYPE_ALLOCATION, + TYPE_SPLIT, + TYPE_COUNT + } type; + Node* parent; + Node* buddy; + + union + { + struct + { + Node* prev; + Node* next; + } free; + struct + { + void* userData; + } allocation; + struct + { + Node* leftChild; + } split; + }; + }; + + // Size of the memory block aligned down to a power of two. + VkDeviceSize m_UsableSize; + uint32_t m_LevelCount; + VmaPoolAllocator m_NodeAllocator; + Node* m_Root; + struct + { + Node* front; + Node* back; + } m_FreeList[MAX_LEVELS]; + + // Number of nodes in the tree with type == TYPE_ALLOCATION. + size_t m_AllocationCount; + // Number of nodes in the tree with type == TYPE_FREE. + size_t m_FreeCount; + // Doesn't include space wasted due to internal fragmentation - allocation sizes are just aligned up to node sizes. + // Doesn't include unusable size. + VkDeviceSize m_SumFreeSize; + + VkDeviceSize GetUnusableSize() const { return GetSize() - m_UsableSize; } + VkDeviceSize LevelToNodeSize(uint32_t level) const { return m_UsableSize >> level; } + + VkDeviceSize AlignAllocationSize(VkDeviceSize size) const + { + if (!IsVirtual()) + { + size = VmaAlignUp(size, (VkDeviceSize)16); + } + return VmaNextPow2(size); + } + Node* FindAllocationNode(VkDeviceSize offset, uint32_t& outLevel) const; + void DeleteNodeChildren(Node* node); + bool ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const; + uint32_t AllocSizeToLevel(VkDeviceSize allocSize) const; + void AddNodeToDetailedStatistics(VmaDetailedStatistics& inoutStats, const Node* node, VkDeviceSize levelNodeSize) const; + // Adds node to the front of FreeList at given level. + // node->type must be FREE. + // node->free.prev, next can be undefined. + void AddToFreeListFront(uint32_t level, Node* node); + // Removes node from FreeList at given level. + // node->type must be FREE. + // node->free.prev, next stay untouched. + void RemoveFromFreeList(uint32_t level, Node* node); + void DebugLogAllAllocationNode(Node* node, uint32_t level) const; + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const; +#endif +}; + +#ifndef _VMA_BLOCK_METADATA_BUDDY_FUNCTIONS +VmaBlockMetadata_Buddy::VmaBlockMetadata_Buddy(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual) + : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual), + m_NodeAllocator(pAllocationCallbacks, 32), // firstBlockCapacity + m_Root(VMA_NULL), + m_AllocationCount(0), + m_FreeCount(1), + m_SumFreeSize(0) +{ + memset(m_FreeList, 0, sizeof(m_FreeList)); +} + +VmaBlockMetadata_Buddy::~VmaBlockMetadata_Buddy() +{ + DeleteNodeChildren(m_Root); + m_NodeAllocator.Free(m_Root); +} + +void VmaBlockMetadata_Buddy::Init(VkDeviceSize size) +{ + VmaBlockMetadata::Init(size); + + m_UsableSize = VmaPrevPow2(size); + m_SumFreeSize = m_UsableSize; + + // Calculate m_LevelCount. + const VkDeviceSize minNodeSize = IsVirtual() ? 1 : 16; + m_LevelCount = 1; + while (m_LevelCount < MAX_LEVELS && + LevelToNodeSize(m_LevelCount) >= minNodeSize) + { + ++m_LevelCount; + } + + Node* rootNode = m_NodeAllocator.Alloc(); + rootNode->offset = 0; + rootNode->type = Node::TYPE_FREE; + rootNode->parent = VMA_NULL; + rootNode->buddy = VMA_NULL; + + m_Root = rootNode; + AddToFreeListFront(0, rootNode); +} + +bool VmaBlockMetadata_Buddy::Validate() const +{ + // Validate tree. + ValidationContext ctx; + if (!ValidateNode(ctx, VMA_NULL, m_Root, 0, LevelToNodeSize(0))) + { + VMA_VALIDATE(false && "ValidateNode failed."); + } + VMA_VALIDATE(m_AllocationCount == ctx.calculatedAllocationCount); + VMA_VALIDATE(m_SumFreeSize == ctx.calculatedSumFreeSize); + + // Validate free node lists. + for (uint32_t level = 0; level < m_LevelCount; ++level) + { + VMA_VALIDATE(m_FreeList[level].front == VMA_NULL || + m_FreeList[level].front->free.prev == VMA_NULL); + + for (Node* node = m_FreeList[level].front; + node != VMA_NULL; + node = node->free.next) + { + VMA_VALIDATE(node->type == Node::TYPE_FREE); + + if (node->free.next == VMA_NULL) + { + VMA_VALIDATE(m_FreeList[level].back == node); + } + else + { + VMA_VALIDATE(node->free.next->free.prev == node); + } + } + } + + // Validate that free lists ar higher levels are empty. + for (uint32_t level = m_LevelCount; level < MAX_LEVELS; ++level) + { + VMA_VALIDATE(m_FreeList[level].front == VMA_NULL && m_FreeList[level].back == VMA_NULL); + } + + return true; +} + +void VmaBlockMetadata_Buddy::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const +{ + inoutStats.statistics.blockCount++; + inoutStats.statistics.blockBytes += GetSize(); + + AddNodeToDetailedStatistics(inoutStats, m_Root, LevelToNodeSize(0)); + + const VkDeviceSize unusableSize = GetUnusableSize(); + if (unusableSize > 0) + VmaAddDetailedStatisticsUnusedRange(inoutStats, unusableSize); +} + +void VmaBlockMetadata_Buddy::AddStatistics(VmaStatistics& inoutStats) const +{ + inoutStats.blockCount++; + inoutStats.allocationCount += (uint32_t)m_AllocationCount; + inoutStats.blockBytes += GetSize(); + inoutStats.allocationBytes += GetSize() - m_SumFreeSize; +} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockMetadata_Buddy::PrintDetailedMap(class VmaJsonWriter& json, uint32_t mapRefCount) const +{ + VmaDetailedStatistics stats; + VmaClearDetailedStatistics(stats); + AddDetailedStatistics(stats); + + PrintDetailedMap_Begin( + json, + stats.statistics.blockBytes - stats.statistics.allocationBytes, + stats.statistics.allocationCount, + stats.unusedRangeCount, + mapRefCount); + + PrintDetailedMapNode(json, m_Root, LevelToNodeSize(0)); + + const VkDeviceSize unusableSize = GetUnusableSize(); + if (unusableSize > 0) + { + PrintDetailedMap_UnusedRange(json, + m_UsableSize, // offset + unusableSize); // size + } + + PrintDetailedMap_End(json); +} +#endif // VMA_STATS_STRING_ENABLED + +bool VmaBlockMetadata_Buddy::CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) +{ + VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm."); + + allocSize = AlignAllocationSize(allocSize); + + // Simple way to respect bufferImageGranularity. May be optimized some day. + // Whenever it might be an OPTIMAL image... + if (allocType == VMA_SUBALLOCATION_TYPE_UNKNOWN || + allocType == VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN || + allocType == VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL) + { + allocAlignment = VMA_MAX(allocAlignment, GetBufferImageGranularity()); + allocSize = VmaAlignUp(allocSize, GetBufferImageGranularity()); + } + + if (allocSize > m_UsableSize) + { + return false; + } + + const uint32_t targetLevel = AllocSizeToLevel(allocSize); + for (uint32_t level = targetLevel; level--; ) + { + for (Node* freeNode = m_FreeList[level].front; + freeNode != VMA_NULL; + freeNode = freeNode->free.next) + { + if (freeNode->offset % allocAlignment == 0) + { + pAllocationRequest->type = VmaAllocationRequestType::Normal; + pAllocationRequest->allocHandle = (VmaAllocHandle)(freeNode->offset + 1); + pAllocationRequest->size = allocSize; + pAllocationRequest->customData = (void*)(uintptr_t)level; + return true; + } + } + } + + return false; +} + +void VmaBlockMetadata_Buddy::Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) +{ + VMA_ASSERT(request.type == VmaAllocationRequestType::Normal); + + const uint32_t targetLevel = AllocSizeToLevel(request.size); + uint32_t currLevel = (uint32_t)(uintptr_t)request.customData; + + Node* currNode = m_FreeList[currLevel].front; + VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE); + const VkDeviceSize offset = (VkDeviceSize)request.allocHandle - 1; + while (currNode->offset != offset) + { + currNode = currNode->free.next; + VMA_ASSERT(currNode != VMA_NULL && currNode->type == Node::TYPE_FREE); + } + + // Go down, splitting free nodes. + while (currLevel < targetLevel) + { + // currNode is already first free node at currLevel. + // Remove it from list of free nodes at this currLevel. + RemoveFromFreeList(currLevel, currNode); + + const uint32_t childrenLevel = currLevel + 1; + + // Create two free sub-nodes. + Node* leftChild = m_NodeAllocator.Alloc(); + Node* rightChild = m_NodeAllocator.Alloc(); + + leftChild->offset = currNode->offset; + leftChild->type = Node::TYPE_FREE; + leftChild->parent = currNode; + leftChild->buddy = rightChild; + + rightChild->offset = currNode->offset + LevelToNodeSize(childrenLevel); + rightChild->type = Node::TYPE_FREE; + rightChild->parent = currNode; + rightChild->buddy = leftChild; + + // Convert current currNode to split type. + currNode->type = Node::TYPE_SPLIT; + currNode->split.leftChild = leftChild; + + // Add child nodes to free list. Order is important! + AddToFreeListFront(childrenLevel, rightChild); + AddToFreeListFront(childrenLevel, leftChild); + + ++m_FreeCount; + ++currLevel; + currNode = m_FreeList[currLevel].front; + + /* + We can be sure that currNode, as left child of node previously split, + also fulfills the alignment requirement. + */ + } + + // Remove from free list. + VMA_ASSERT(currLevel == targetLevel && + currNode != VMA_NULL && + currNode->type == Node::TYPE_FREE); + RemoveFromFreeList(currLevel, currNode); + + // Convert to allocation node. + currNode->type = Node::TYPE_ALLOCATION; + currNode->allocation.userData = userData; + + ++m_AllocationCount; + --m_FreeCount; + m_SumFreeSize -= request.size; +} + +void VmaBlockMetadata_Buddy::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) +{ + uint32_t level = 0; + outInfo.offset = (VkDeviceSize)allocHandle - 1; + const Node* const node = FindAllocationNode(outInfo.offset, level); + outInfo.size = LevelToNodeSize(level); + outInfo.pUserData = node->allocation.userData; +} + +void* VmaBlockMetadata_Buddy::GetAllocationUserData(VmaAllocHandle allocHandle) const +{ + uint32_t level = 0; + const Node* const node = FindAllocationNode((VkDeviceSize)allocHandle - 1, level); + return node->allocation.userData; +} + +VmaAllocHandle VmaBlockMetadata_Buddy::GetAllocationListBegin() const +{ + // Function only used for defragmentation, which is disabled for this algorithm + return VK_NULL_HANDLE; +} + +VmaAllocHandle VmaBlockMetadata_Buddy::GetNextAllocation(VmaAllocHandle prevAlloc) const +{ + // Function only used for defragmentation, which is disabled for this algorithm + return VK_NULL_HANDLE; +} + +void VmaBlockMetadata_Buddy::DeleteNodeChildren(Node* node) +{ + if (node->type == Node::TYPE_SPLIT) + { + DeleteNodeChildren(node->split.leftChild->buddy); + DeleteNodeChildren(node->split.leftChild); + const VkAllocationCallbacks* allocationCallbacks = GetAllocationCallbacks(); + m_NodeAllocator.Free(node->split.leftChild->buddy); + m_NodeAllocator.Free(node->split.leftChild); + } +} + +void VmaBlockMetadata_Buddy::Clear() +{ + DeleteNodeChildren(m_Root); + m_Root->type = Node::TYPE_FREE; + m_AllocationCount = 0; + m_FreeCount = 1; + m_SumFreeSize = m_UsableSize; +} + +void VmaBlockMetadata_Buddy::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) +{ + uint32_t level = 0; + Node* const node = FindAllocationNode((VkDeviceSize)allocHandle - 1, level); + node->allocation.userData = userData; +} + +VmaBlockMetadata_Buddy::Node* VmaBlockMetadata_Buddy::FindAllocationNode(VkDeviceSize offset, uint32_t& outLevel) const +{ + Node* node = m_Root; + VkDeviceSize nodeOffset = 0; + outLevel = 0; + VkDeviceSize levelNodeSize = LevelToNodeSize(0); + while (node->type == Node::TYPE_SPLIT) + { + const VkDeviceSize nextLevelNodeSize = levelNodeSize >> 1; + if (offset < nodeOffset + nextLevelNodeSize) + { + node = node->split.leftChild; + } + else + { + node = node->split.leftChild->buddy; + nodeOffset += nextLevelNodeSize; + } + ++outLevel; + levelNodeSize = nextLevelNodeSize; + } + + VMA_ASSERT(node != VMA_NULL && node->type == Node::TYPE_ALLOCATION); + return node; +} + +bool VmaBlockMetadata_Buddy::ValidateNode(ValidationContext& ctx, const Node* parent, const Node* curr, uint32_t level, VkDeviceSize levelNodeSize) const +{ + VMA_VALIDATE(level < m_LevelCount); + VMA_VALIDATE(curr->parent == parent); + VMA_VALIDATE((curr->buddy == VMA_NULL) == (parent == VMA_NULL)); + VMA_VALIDATE(curr->buddy == VMA_NULL || curr->buddy->buddy == curr); + switch (curr->type) + { + case Node::TYPE_FREE: + // curr->free.prev, next are validated separately. + ctx.calculatedSumFreeSize += levelNodeSize; + ++ctx.calculatedFreeCount; + break; + case Node::TYPE_ALLOCATION: + ++ctx.calculatedAllocationCount; + if (!IsVirtual()) + { + VMA_VALIDATE(curr->allocation.userData != VMA_NULL); + } + break; + case Node::TYPE_SPLIT: + { + const uint32_t childrenLevel = level + 1; + const VkDeviceSize childrenLevelNodeSize = levelNodeSize >> 1; + const Node* const leftChild = curr->split.leftChild; + VMA_VALIDATE(leftChild != VMA_NULL); + VMA_VALIDATE(leftChild->offset == curr->offset); + if (!ValidateNode(ctx, curr, leftChild, childrenLevel, childrenLevelNodeSize)) + { + VMA_VALIDATE(false && "ValidateNode for left child failed."); + } + const Node* const rightChild = leftChild->buddy; + VMA_VALIDATE(rightChild->offset == curr->offset + childrenLevelNodeSize); + if (!ValidateNode(ctx, curr, rightChild, childrenLevel, childrenLevelNodeSize)) + { + VMA_VALIDATE(false && "ValidateNode for right child failed."); + } + } + break; + default: + return false; + } + + return true; +} + +uint32_t VmaBlockMetadata_Buddy::AllocSizeToLevel(VkDeviceSize allocSize) const +{ + // I know this could be optimized somehow e.g. by using std::log2p1 from C++20. + uint32_t level = 0; + VkDeviceSize currLevelNodeSize = m_UsableSize; + VkDeviceSize nextLevelNodeSize = currLevelNodeSize >> 1; + while (allocSize <= nextLevelNodeSize && level + 1 < m_LevelCount) + { + ++level; + currLevelNodeSize >>= 1; + nextLevelNodeSize >>= 1; + } + return level; +} + +void VmaBlockMetadata_Buddy::Free(VmaAllocHandle allocHandle) +{ + uint32_t level = 0; + Node* node = FindAllocationNode((VkDeviceSize)allocHandle - 1, level); + + ++m_FreeCount; + --m_AllocationCount; + m_SumFreeSize += LevelToNodeSize(level); + + node->type = Node::TYPE_FREE; + + // Join free nodes if possible. + while (level > 0 && node->buddy->type == Node::TYPE_FREE) + { + RemoveFromFreeList(level, node->buddy); + Node* const parent = node->parent; + + m_NodeAllocator.Free(node->buddy); + m_NodeAllocator.Free(node); + parent->type = Node::TYPE_FREE; + + node = parent; + --level; + --m_FreeCount; + } + + AddToFreeListFront(level, node); +} + +void VmaBlockMetadata_Buddy::AddNodeToDetailedStatistics(VmaDetailedStatistics& inoutStats, const Node* node, VkDeviceSize levelNodeSize) const +{ + switch (node->type) + { + case Node::TYPE_FREE: + VmaAddDetailedStatisticsUnusedRange(inoutStats, levelNodeSize); + break; + case Node::TYPE_ALLOCATION: + VmaAddDetailedStatisticsAllocation(inoutStats, levelNodeSize); + break; + case Node::TYPE_SPLIT: + { + const VkDeviceSize childrenNodeSize = levelNodeSize / 2; + const Node* const leftChild = node->split.leftChild; + AddNodeToDetailedStatistics(inoutStats, leftChild, childrenNodeSize); + const Node* const rightChild = leftChild->buddy; + AddNodeToDetailedStatistics(inoutStats, rightChild, childrenNodeSize); + } + break; + default: + VMA_ASSERT(0); + } +} + +void VmaBlockMetadata_Buddy::AddToFreeListFront(uint32_t level, Node* node) +{ + VMA_ASSERT(node->type == Node::TYPE_FREE); + + // List is empty. + Node* const frontNode = m_FreeList[level].front; + if (frontNode == VMA_NULL) + { + VMA_ASSERT(m_FreeList[level].back == VMA_NULL); + node->free.prev = node->free.next = VMA_NULL; + m_FreeList[level].front = m_FreeList[level].back = node; + } + else + { + VMA_ASSERT(frontNode->free.prev == VMA_NULL); + node->free.prev = VMA_NULL; + node->free.next = frontNode; + frontNode->free.prev = node; + m_FreeList[level].front = node; + } +} + +void VmaBlockMetadata_Buddy::RemoveFromFreeList(uint32_t level, Node* node) +{ + VMA_ASSERT(m_FreeList[level].front != VMA_NULL); + + // It is at the front. + if (node->free.prev == VMA_NULL) + { + VMA_ASSERT(m_FreeList[level].front == node); + m_FreeList[level].front = node->free.next; + } + else + { + Node* const prevFreeNode = node->free.prev; + VMA_ASSERT(prevFreeNode->free.next == node); + prevFreeNode->free.next = node->free.next; + } + + // It is at the back. + if (node->free.next == VMA_NULL) + { + VMA_ASSERT(m_FreeList[level].back == node); + m_FreeList[level].back = node->free.prev; + } + else + { + Node* const nextFreeNode = node->free.next; + VMA_ASSERT(nextFreeNode->free.prev == node); + nextFreeNode->free.prev = node->free.prev; + } +} + +void VmaBlockMetadata_Buddy::DebugLogAllAllocationNode(Node* node, uint32_t level) const +{ + switch (node->type) + { + case Node::TYPE_FREE: + break; + case Node::TYPE_ALLOCATION: + DebugLogAllocation(node->offset, LevelToNodeSize(level), node->allocation.userData); + break; + case Node::TYPE_SPLIT: + { + ++level; + DebugLogAllAllocationNode(node->split.leftChild, level); + DebugLogAllAllocationNode(node->split.leftChild->buddy, level); + } + break; + default: + VMA_ASSERT(0); + } +} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockMetadata_Buddy::PrintDetailedMapNode(class VmaJsonWriter& json, const Node* node, VkDeviceSize levelNodeSize) const +{ + switch (node->type) + { + case Node::TYPE_FREE: + PrintDetailedMap_UnusedRange(json, node->offset, levelNodeSize); + break; + case Node::TYPE_ALLOCATION: + PrintDetailedMap_Allocation(json, node->offset, levelNodeSize, node->allocation.userData); + break; + case Node::TYPE_SPLIT: + { + const VkDeviceSize childrenNodeSize = levelNodeSize / 2; + const Node* const leftChild = node->split.leftChild; + PrintDetailedMapNode(json, leftChild, childrenNodeSize); + const Node* const rightChild = leftChild->buddy; + PrintDetailedMapNode(json, rightChild, childrenNodeSize); + } + break; + default: + VMA_ASSERT(0); + } +} +#endif // VMA_STATS_STRING_ENABLED +#endif // _VMA_BLOCK_METADATA_BUDDY_FUNCTIONS +#endif // _VMA_BLOCK_METADATA_BUDDY +#endif // #if 0 + +#ifndef _VMA_BLOCK_METADATA_TLSF +// To not search current larger region if first allocation won't succeed and skip to smaller range +// use with VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT as strategy in CreateAllocationRequest(). +// When fragmentation and reusal of previous blocks doesn't matter then use with +// VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT for fastest alloc time possible. +class VmaBlockMetadata_TLSF : public VmaBlockMetadata +{ + VMA_CLASS_NO_COPY(VmaBlockMetadata_TLSF) +public: + VmaBlockMetadata_TLSF(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual); + virtual ~VmaBlockMetadata_TLSF(); + + size_t GetAllocationCount() const override { return m_AllocCount; } + size_t GetFreeRegionsCount() const override { return m_BlocksFreeCount + 1; } + VkDeviceSize GetSumFreeSize() const override { return m_BlocksFreeSize + m_NullBlock->size; } + bool IsEmpty() const override { return m_NullBlock->offset == 0; } + VkDeviceSize GetAllocationOffset(VmaAllocHandle allocHandle) const override { return ((Block*)allocHandle)->offset; } + + void Init(VkDeviceSize size) override; + bool Validate() const override; + + void AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const override; + void AddStatistics(VmaStatistics& inoutStats) const override; + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap(class VmaJsonWriter& json) const override; +#endif + + bool CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) override; + + VkResult CheckCorruption(const void* pBlockData) override; + void Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) override; + + void Free(VmaAllocHandle allocHandle) override; + void GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) override; + void* GetAllocationUserData(VmaAllocHandle allocHandle) const override; + VmaAllocHandle GetAllocationListBegin() const override; + VmaAllocHandle GetNextAllocation(VmaAllocHandle prevAlloc) const override; + VkDeviceSize GetNextFreeRegionSize(VmaAllocHandle alloc) const override; + void Clear() override; + void SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) override; + void DebugLogAllAllocations() const override; + +private: + // According to original paper it should be preferable 4 or 5: + // M. Masmano, I. Ripoll, A. Crespo, and J. Real "TLSF: a New Dynamic Memory Allocator for Real-Time Systems" + // http://www.gii.upv.es/tlsf/files/ecrts04_tlsf.pdf + static const uint8_t SECOND_LEVEL_INDEX = 5; + static const uint16_t SMALL_BUFFER_SIZE = 256; + static const uint32_t INITIAL_BLOCK_ALLOC_COUNT = 16; + static const uint8_t MEMORY_CLASS_SHIFT = 7; + static const uint8_t MAX_MEMORY_CLASSES = 65 - MEMORY_CLASS_SHIFT; + + class Block + { + public: + VkDeviceSize offset; + VkDeviceSize size; + Block* prevPhysical; + Block* nextPhysical; + + void MarkFree() { prevFree = VMA_NULL; } + void MarkTaken() { prevFree = this; } + bool IsFree() const { return prevFree != this; } + void*& UserData() { VMA_HEAVY_ASSERT(!IsFree()); return userData; } + Block*& PrevFree() { return prevFree; } + Block*& NextFree() { VMA_HEAVY_ASSERT(IsFree()); return nextFree; } + + private: + Block* prevFree; // Address of the same block here indicates that block is taken + union + { + Block* nextFree; + void* userData; + }; + }; + + size_t m_AllocCount; + // Total number of free blocks besides null block + size_t m_BlocksFreeCount; + // Total size of free blocks excluding null block + VkDeviceSize m_BlocksFreeSize; + uint32_t m_IsFreeBitmap; + uint8_t m_MemoryClasses; + uint32_t m_InnerIsFreeBitmap[MAX_MEMORY_CLASSES]; + uint32_t m_ListsCount; + /* + * 0: 0-3 lists for small buffers + * 1+: 0-(2^SLI-1) lists for normal buffers + */ + Block** m_FreeList; + VmaPoolAllocator m_BlockAllocator; + Block* m_NullBlock; + VmaBlockBufferImageGranularity m_GranularityHandler; + + uint8_t SizeToMemoryClass(VkDeviceSize size) const; + uint16_t SizeToSecondIndex(VkDeviceSize size, uint8_t memoryClass) const; + uint32_t GetListIndex(uint8_t memoryClass, uint16_t secondIndex) const; + uint32_t GetListIndex(VkDeviceSize size) const; + + void RemoveFreeBlock(Block* block); + void InsertFreeBlock(Block* block); + void MergeBlock(Block* block, Block* prev); + + Block* FindFreeBlock(VkDeviceSize size, uint32_t& listIndex) const; + bool CheckBlock( + Block& block, + uint32_t listIndex, + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + VmaAllocationRequest* pAllocationRequest); +}; + +#ifndef _VMA_BLOCK_METADATA_TLSF_FUNCTIONS +VmaBlockMetadata_TLSF::VmaBlockMetadata_TLSF(const VkAllocationCallbacks* pAllocationCallbacks, + VkDeviceSize bufferImageGranularity, bool isVirtual) + : VmaBlockMetadata(pAllocationCallbacks, bufferImageGranularity, isVirtual), + m_AllocCount(0), + m_BlocksFreeCount(0), + m_BlocksFreeSize(0), + m_IsFreeBitmap(0), + m_MemoryClasses(0), + m_ListsCount(0), + m_FreeList(VMA_NULL), + m_BlockAllocator(pAllocationCallbacks, INITIAL_BLOCK_ALLOC_COUNT), + m_NullBlock(VMA_NULL), + m_GranularityHandler(bufferImageGranularity) {} + +VmaBlockMetadata_TLSF::~VmaBlockMetadata_TLSF() +{ + if (m_FreeList) + vma_delete_array(GetAllocationCallbacks(), m_FreeList, m_ListsCount); + m_GranularityHandler.Destroy(GetAllocationCallbacks()); +} + +void VmaBlockMetadata_TLSF::Init(VkDeviceSize size) +{ + VmaBlockMetadata::Init(size); + + if (!IsVirtual()) + m_GranularityHandler.Init(GetAllocationCallbacks(), size); + + m_NullBlock = m_BlockAllocator.Alloc(); + m_NullBlock->size = size; + m_NullBlock->offset = 0; + m_NullBlock->prevPhysical = VMA_NULL; + m_NullBlock->nextPhysical = VMA_NULL; + m_NullBlock->MarkFree(); + m_NullBlock->NextFree() = VMA_NULL; + m_NullBlock->PrevFree() = VMA_NULL; + uint8_t memoryClass = SizeToMemoryClass(size); + uint16_t sli = SizeToSecondIndex(size, memoryClass); + m_ListsCount = (memoryClass == 0 ? 0 : (memoryClass - 1) * (1UL << SECOND_LEVEL_INDEX) + sli) + 1; + if (IsVirtual()) + m_ListsCount += 1UL << SECOND_LEVEL_INDEX; + else + m_ListsCount += 4; + + m_MemoryClasses = memoryClass + 2; + memset(m_InnerIsFreeBitmap, 0, MAX_MEMORY_CLASSES * sizeof(uint32_t)); + + m_FreeList = vma_new_array(GetAllocationCallbacks(), Block*, m_ListsCount); + memset(m_FreeList, 0, m_ListsCount * sizeof(Block*)); +} + +bool VmaBlockMetadata_TLSF::Validate() const +{ + VMA_VALIDATE(GetSumFreeSize() <= GetSize()); + + VkDeviceSize calculatedSize = m_NullBlock->size; + VkDeviceSize calculatedFreeSize = m_NullBlock->size; + size_t allocCount = 0; + size_t freeCount = 0; + + // Check integrity of free lists + for (uint32_t list = 0; list < m_ListsCount; ++list) + { + Block* block = m_FreeList[list]; + if (block != VMA_NULL) + { + VMA_VALIDATE(block->IsFree()); + VMA_VALIDATE(block->PrevFree() == VMA_NULL); + while (block->NextFree()) + { + VMA_VALIDATE(block->NextFree()->IsFree()); + VMA_VALIDATE(block->NextFree()->PrevFree() == block); + block = block->NextFree(); + } + } + } + + VkDeviceSize nextOffset = m_NullBlock->offset; + auto validateCtx = m_GranularityHandler.StartValidation(GetAllocationCallbacks(), IsVirtual()); + + VMA_VALIDATE(m_NullBlock->nextPhysical == VMA_NULL); + if (m_NullBlock->prevPhysical) + { + VMA_VALIDATE(m_NullBlock->prevPhysical->nextPhysical == m_NullBlock); + } + // Check all blocks + for (Block* prev = m_NullBlock->prevPhysical; prev != VMA_NULL; prev = prev->prevPhysical) + { + VMA_VALIDATE(prev->offset + prev->size == nextOffset); + nextOffset = prev->offset; + calculatedSize += prev->size; + + uint32_t listIndex = GetListIndex(prev->size); + if (prev->IsFree()) + { + ++freeCount; + // Check if free block belongs to free list + Block* freeBlock = m_FreeList[listIndex]; + VMA_VALIDATE(freeBlock != VMA_NULL); + + bool found = false; + do + { + if (freeBlock == prev) + found = true; + + freeBlock = freeBlock->NextFree(); + } while (!found && freeBlock != VMA_NULL); + + VMA_VALIDATE(found); + calculatedFreeSize += prev->size; + } + else + { + ++allocCount; + // Check if taken block is not on a free list + Block* freeBlock = m_FreeList[listIndex]; + while (freeBlock) + { + VMA_VALIDATE(freeBlock != prev); + freeBlock = freeBlock->NextFree(); + } + + if (!IsVirtual()) + { + VMA_VALIDATE(m_GranularityHandler.Validate(validateCtx, prev->offset, prev->size)); + } + } + + if (prev->prevPhysical) + { + VMA_VALIDATE(prev->prevPhysical->nextPhysical == prev); + } + } + + if (!IsVirtual()) + { + VMA_VALIDATE(m_GranularityHandler.FinishValidation(validateCtx)); + } + + VMA_VALIDATE(nextOffset == 0); + VMA_VALIDATE(calculatedSize == GetSize()); + VMA_VALIDATE(calculatedFreeSize == GetSumFreeSize()); + VMA_VALIDATE(allocCount == m_AllocCount); + VMA_VALIDATE(freeCount == m_BlocksFreeCount); + + return true; +} + +void VmaBlockMetadata_TLSF::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) const +{ + inoutStats.statistics.blockCount++; + inoutStats.statistics.blockBytes += GetSize(); + if (m_NullBlock->size > 0) + VmaAddDetailedStatisticsUnusedRange(inoutStats, m_NullBlock->size); + + for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical) + { + if (block->IsFree()) + VmaAddDetailedStatisticsUnusedRange(inoutStats, block->size); + else + VmaAddDetailedStatisticsAllocation(inoutStats, block->size); + } +} + +void VmaBlockMetadata_TLSF::AddStatistics(VmaStatistics& inoutStats) const +{ + inoutStats.blockCount++; + inoutStats.allocationCount += (uint32_t)m_AllocCount; + inoutStats.blockBytes += GetSize(); + inoutStats.allocationBytes += GetSize() - GetSumFreeSize(); +} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockMetadata_TLSF::PrintDetailedMap(class VmaJsonWriter& json) const +{ + size_t blockCount = m_AllocCount + m_BlocksFreeCount; + VmaStlAllocator allocator(GetAllocationCallbacks()); + VmaVector> blockList(blockCount, allocator); + + size_t i = blockCount; + for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical) + { + blockList[--i] = block; + } + VMA_ASSERT(i == 0); + + VmaDetailedStatistics stats; + VmaClearDetailedStatistics(stats); + AddDetailedStatistics(stats); + + PrintDetailedMap_Begin(json, + stats.statistics.blockBytes - stats.statistics.allocationBytes, + stats.statistics.allocationCount, + stats.unusedRangeCount); + + for (; i < blockCount; ++i) + { + Block* block = blockList[i]; + if (block->IsFree()) + PrintDetailedMap_UnusedRange(json, block->offset, block->size); + else + PrintDetailedMap_Allocation(json, block->offset, block->size, block->UserData()); + } + if (m_NullBlock->size > 0) + PrintDetailedMap_UnusedRange(json, m_NullBlock->offset, m_NullBlock->size); + + PrintDetailedMap_End(json); +} +#endif + +bool VmaBlockMetadata_TLSF::CreateAllocationRequest( + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + bool upperAddress, + VmaSuballocationType allocType, + uint32_t strategy, + VmaAllocationRequest* pAllocationRequest) +{ + VMA_ASSERT(allocSize > 0 && "Cannot allocate empty block!"); + VMA_ASSERT(!upperAddress && "VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT can be used only with linear algorithm."); + + // For small granularity round up + if (!IsVirtual()) + m_GranularityHandler.RoundupAllocRequest(allocType, allocSize, allocAlignment); + + allocSize += GetDebugMargin(); + // Quick check for too small pool + if (allocSize > GetSumFreeSize()) + return false; + + // If no free blocks in pool then check only null block + if (m_BlocksFreeCount == 0) + return CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest); + + // Round up to the next block + VkDeviceSize sizeForNextList = allocSize; + VkDeviceSize smallSizeStep = SMALL_BUFFER_SIZE / (IsVirtual() ? 1 << SECOND_LEVEL_INDEX : 4); + if (allocSize > SMALL_BUFFER_SIZE) + { + sizeForNextList += (1ULL << (VMA_BITSCAN_MSB(allocSize) - SECOND_LEVEL_INDEX)); + } + else if (allocSize > SMALL_BUFFER_SIZE - smallSizeStep) + sizeForNextList = SMALL_BUFFER_SIZE + 1; + else + sizeForNextList += smallSizeStep; + + uint32_t nextListIndex = 0; + uint32_t prevListIndex = 0; + Block* nextListBlock = VMA_NULL; + Block* prevListBlock = VMA_NULL; + + // Check blocks according to strategies + if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT) + { + // Quick check for larger block first + nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex); + if (nextListBlock != VMA_NULL && CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + + // If not fitted then null block + if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + + // Null block failed, search larger bucket + while (nextListBlock) + { + if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + nextListBlock = nextListBlock->NextFree(); + } + + // Failed again, check best fit bucket + prevListBlock = FindFreeBlock(allocSize, prevListIndex); + while (prevListBlock) + { + if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + prevListBlock = prevListBlock->NextFree(); + } + } + else if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_MEMORY_BIT) + { + // Check best fit bucket + prevListBlock = FindFreeBlock(allocSize, prevListIndex); + while (prevListBlock) + { + if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + prevListBlock = prevListBlock->NextFree(); + } + + // If failed check null block + if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + + // Check larger bucket + nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex); + while (nextListBlock) + { + if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + nextListBlock = nextListBlock->NextFree(); + } + } + else if (strategy & VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT ) + { + // Perform search from the start + VmaStlAllocator allocator(GetAllocationCallbacks()); + VmaVector> blockList(m_BlocksFreeCount, allocator); + + size_t i = m_BlocksFreeCount; + for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical) + { + if (block->IsFree() && block->size >= allocSize) + blockList[--i] = block; + } + + for (; i < m_BlocksFreeCount; ++i) + { + Block& block = *blockList[i]; + if (CheckBlock(block, GetListIndex(block.size), allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + } + + // If failed check null block + if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + + // Whole range searched, no more memory + return false; + } + else + { + // Check larger bucket + nextListBlock = FindFreeBlock(sizeForNextList, nextListIndex); + while (nextListBlock) + { + if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + nextListBlock = nextListBlock->NextFree(); + } + + // If failed check null block + if (CheckBlock(*m_NullBlock, m_ListsCount, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + + // Check best fit bucket + prevListBlock = FindFreeBlock(allocSize, prevListIndex); + while (prevListBlock) + { + if (CheckBlock(*prevListBlock, prevListIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + prevListBlock = prevListBlock->NextFree(); + } + } + + // Worst case, full search has to be done + while (++nextListIndex < m_ListsCount) + { + nextListBlock = m_FreeList[nextListIndex]; + while (nextListBlock) + { + if (CheckBlock(*nextListBlock, nextListIndex, allocSize, allocAlignment, allocType, pAllocationRequest)) + return true; + nextListBlock = nextListBlock->NextFree(); + } + } + + // No more memory sadly + return false; +} + +VkResult VmaBlockMetadata_TLSF::CheckCorruption(const void* pBlockData) +{ + for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical) + { + if (!block->IsFree()) + { + if (!VmaValidateMagicValue(pBlockData, block->offset + block->size)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER VALIDATED ALLOCATION!"); + return VK_ERROR_UNKNOWN_COPY; + } + } + } + + return VK_SUCCESS; +} + +void VmaBlockMetadata_TLSF::Alloc( + const VmaAllocationRequest& request, + VmaSuballocationType type, + void* userData) +{ + VMA_ASSERT(request.type == VmaAllocationRequestType::TLSF); + + // Get block and pop it from the free list + Block* currentBlock = (Block*)request.allocHandle; + VkDeviceSize offset = request.algorithmData; + VMA_ASSERT(currentBlock != VMA_NULL); + VMA_ASSERT(currentBlock->offset <= offset); + + if (currentBlock != m_NullBlock) + RemoveFreeBlock(currentBlock); + + VkDeviceSize debugMargin = GetDebugMargin(); + VkDeviceSize misssingAlignment = offset - currentBlock->offset; + + // Append missing alignment to prev block or create new one + if (misssingAlignment) + { + Block* prevBlock = currentBlock->prevPhysical; + VMA_ASSERT(prevBlock != VMA_NULL && "There should be no missing alignment at offset 0!"); + + if (prevBlock->IsFree() && prevBlock->size != debugMargin) + { + uint32_t oldList = GetListIndex(prevBlock->size); + prevBlock->size += misssingAlignment; + // Check if new size crosses list bucket + if (oldList != GetListIndex(prevBlock->size)) + { + prevBlock->size -= misssingAlignment; + RemoveFreeBlock(prevBlock); + prevBlock->size += misssingAlignment; + InsertFreeBlock(prevBlock); + } + else + m_BlocksFreeSize += misssingAlignment; + } + else + { + Block* newBlock = m_BlockAllocator.Alloc(); + currentBlock->prevPhysical = newBlock; + prevBlock->nextPhysical = newBlock; + newBlock->prevPhysical = prevBlock; + newBlock->nextPhysical = currentBlock; + newBlock->size = misssingAlignment; + newBlock->offset = currentBlock->offset; + newBlock->MarkTaken(); + + InsertFreeBlock(newBlock); + } + + currentBlock->size -= misssingAlignment; + currentBlock->offset += misssingAlignment; + } + + VkDeviceSize size = request.size + debugMargin; + if (currentBlock->size == size) + { + if (currentBlock == m_NullBlock) + { + // Setup new null block + m_NullBlock = m_BlockAllocator.Alloc(); + m_NullBlock->size = 0; + m_NullBlock->offset = currentBlock->offset + size; + m_NullBlock->prevPhysical = currentBlock; + m_NullBlock->nextPhysical = VMA_NULL; + m_NullBlock->MarkFree(); + m_NullBlock->PrevFree() = VMA_NULL; + m_NullBlock->NextFree() = VMA_NULL; + currentBlock->nextPhysical = m_NullBlock; + currentBlock->MarkTaken(); + } + } + else + { + VMA_ASSERT(currentBlock->size > size && "Proper block already found, shouldn't find smaller one!"); + + // Create new free block + Block* newBlock = m_BlockAllocator.Alloc(); + newBlock->size = currentBlock->size - size; + newBlock->offset = currentBlock->offset + size; + newBlock->prevPhysical = currentBlock; + newBlock->nextPhysical = currentBlock->nextPhysical; + currentBlock->nextPhysical = newBlock; + currentBlock->size = size; + + if (currentBlock == m_NullBlock) + { + m_NullBlock = newBlock; + m_NullBlock->MarkFree(); + m_NullBlock->NextFree() = VMA_NULL; + m_NullBlock->PrevFree() = VMA_NULL; + currentBlock->MarkTaken(); + } + else + { + newBlock->nextPhysical->prevPhysical = newBlock; + newBlock->MarkTaken(); + InsertFreeBlock(newBlock); + } + } + currentBlock->UserData() = userData; + + if (debugMargin > 0) + { + currentBlock->size -= debugMargin; + Block* newBlock = m_BlockAllocator.Alloc(); + newBlock->size = debugMargin; + newBlock->offset = currentBlock->offset + currentBlock->size; + newBlock->prevPhysical = currentBlock; + newBlock->nextPhysical = currentBlock->nextPhysical; + newBlock->MarkTaken(); + currentBlock->nextPhysical->prevPhysical = newBlock; + currentBlock->nextPhysical = newBlock; + InsertFreeBlock(newBlock); + } + + if (!IsVirtual()) + m_GranularityHandler.AllocPages((uint8_t)(uintptr_t)request.customData, + currentBlock->offset, currentBlock->size); + ++m_AllocCount; +} + +void VmaBlockMetadata_TLSF::Free(VmaAllocHandle allocHandle) +{ + Block* block = (Block*)allocHandle; + Block* next = block->nextPhysical; + VMA_ASSERT(!block->IsFree() && "Block is already free!"); + + if (!IsVirtual()) + m_GranularityHandler.FreePages(block->offset, block->size); + --m_AllocCount; + + VkDeviceSize debugMargin = GetDebugMargin(); + if (debugMargin > 0) + { + RemoveFreeBlock(next); + MergeBlock(next, block); + block = next; + next = next->nextPhysical; + } + + // Try merging + Block* prev = block->prevPhysical; + if (prev != VMA_NULL && prev->IsFree() && prev->size != debugMargin) + { + RemoveFreeBlock(prev); + MergeBlock(block, prev); + } + + if (!next->IsFree()) + InsertFreeBlock(block); + else if (next == m_NullBlock) + MergeBlock(m_NullBlock, block); + else + { + RemoveFreeBlock(next); + MergeBlock(next, block); + InsertFreeBlock(next); + } +} + +void VmaBlockMetadata_TLSF::GetAllocationInfo(VmaAllocHandle allocHandle, VmaVirtualAllocationInfo& outInfo) +{ + Block* block = (Block*)allocHandle; + VMA_ASSERT(!block->IsFree() && "Cannot get allocation info for free block!"); + outInfo.offset = block->offset; + outInfo.size = block->size; + outInfo.pUserData = block->UserData(); +} + +void* VmaBlockMetadata_TLSF::GetAllocationUserData(VmaAllocHandle allocHandle) const +{ + Block* block = (Block*)allocHandle; + VMA_ASSERT(!block->IsFree() && "Cannot get user data for free block!"); + return block->UserData(); +} + +VmaAllocHandle VmaBlockMetadata_TLSF::GetAllocationListBegin() const +{ + if (m_AllocCount == 0) + return VK_NULL_HANDLE; + + for (Block* block = m_NullBlock->prevPhysical; block; block = block->prevPhysical) + { + if (!block->IsFree()) + return (VmaAllocHandle)block; + } + VMA_ASSERT(false && "If m_AllocCount > 0 then should find any allocation!"); + return VK_NULL_HANDLE; +} + +VmaAllocHandle VmaBlockMetadata_TLSF::GetNextAllocation(VmaAllocHandle prevAlloc) const +{ + Block* startBlock = (Block*)prevAlloc; + VMA_ASSERT(!startBlock->IsFree() && "Incorrect block!"); + + for (Block* block = startBlock->prevPhysical; block; block = block->prevPhysical) + { + if (!block->IsFree()) + return (VmaAllocHandle)block; + } + return VK_NULL_HANDLE; +} + +VkDeviceSize VmaBlockMetadata_TLSF::GetNextFreeRegionSize(VmaAllocHandle alloc) const +{ + Block* block = (Block*)alloc; + VMA_ASSERT(!block->IsFree() && "Incorrect block!"); + + if (block->prevPhysical) + return block->prevPhysical->IsFree() ? block->prevPhysical->size : 0; + return 0; +} + +void VmaBlockMetadata_TLSF::Clear() +{ + m_AllocCount = 0; + m_BlocksFreeCount = 0; + m_BlocksFreeSize = 0; + m_IsFreeBitmap = 0; + m_NullBlock->offset = 0; + m_NullBlock->size = GetSize(); + Block* block = m_NullBlock->prevPhysical; + m_NullBlock->prevPhysical = VMA_NULL; + while (block) + { + Block* prev = block->prevPhysical; + m_BlockAllocator.Free(block); + block = prev; + } + memset(m_FreeList, 0, m_ListsCount * sizeof(Block*)); + memset(m_InnerIsFreeBitmap, 0, m_MemoryClasses * sizeof(uint32_t)); + m_GranularityHandler.Clear(); +} + +void VmaBlockMetadata_TLSF::SetAllocationUserData(VmaAllocHandle allocHandle, void* userData) +{ + Block* block = (Block*)allocHandle; + VMA_ASSERT(!block->IsFree() && "Trying to set user data for not allocated block!"); + block->UserData() = userData; +} + +void VmaBlockMetadata_TLSF::DebugLogAllAllocations() const +{ + for (Block* block = m_NullBlock->prevPhysical; block != VMA_NULL; block = block->prevPhysical) + if (!block->IsFree()) + DebugLogAllocation(block->offset, block->size, block->UserData()); +} + +uint8_t VmaBlockMetadata_TLSF::SizeToMemoryClass(VkDeviceSize size) const +{ + if (size > SMALL_BUFFER_SIZE) + return VMA_BITSCAN_MSB(size) - MEMORY_CLASS_SHIFT; + return 0; +} + +uint16_t VmaBlockMetadata_TLSF::SizeToSecondIndex(VkDeviceSize size, uint8_t memoryClass) const +{ + if (memoryClass == 0) + { + if (IsVirtual()) + return static_cast((size - 1) / 8); + else + return static_cast((size - 1) / 64); + } + return static_cast((size >> (memoryClass + MEMORY_CLASS_SHIFT - SECOND_LEVEL_INDEX)) ^ (1U << SECOND_LEVEL_INDEX)); +} + +uint32_t VmaBlockMetadata_TLSF::GetListIndex(uint8_t memoryClass, uint16_t secondIndex) const +{ + if (memoryClass == 0) + return secondIndex; + + const uint32_t index = static_cast(memoryClass - 1) * (1 << SECOND_LEVEL_INDEX) + secondIndex; + if (IsVirtual()) + return index + (1 << SECOND_LEVEL_INDEX); + else + return index + 4; +} + +uint32_t VmaBlockMetadata_TLSF::GetListIndex(VkDeviceSize size) const +{ + uint8_t memoryClass = SizeToMemoryClass(size); + return GetListIndex(memoryClass, SizeToSecondIndex(size, memoryClass)); +} + +void VmaBlockMetadata_TLSF::RemoveFreeBlock(Block* block) +{ + VMA_ASSERT(block != m_NullBlock); + VMA_ASSERT(block->IsFree()); + + if (block->NextFree() != VMA_NULL) + block->NextFree()->PrevFree() = block->PrevFree(); + if (block->PrevFree() != VMA_NULL) + block->PrevFree()->NextFree() = block->NextFree(); + else + { + uint8_t memClass = SizeToMemoryClass(block->size); + uint16_t secondIndex = SizeToSecondIndex(block->size, memClass); + uint32_t index = GetListIndex(memClass, secondIndex); + VMA_ASSERT(m_FreeList[index] == block); + m_FreeList[index] = block->NextFree(); + if (block->NextFree() == VMA_NULL) + { + m_InnerIsFreeBitmap[memClass] &= ~(1U << secondIndex); + if (m_InnerIsFreeBitmap[memClass] == 0) + m_IsFreeBitmap &= ~(1UL << memClass); + } + } + block->MarkTaken(); + block->UserData() = VMA_NULL; + --m_BlocksFreeCount; + m_BlocksFreeSize -= block->size; +} + +void VmaBlockMetadata_TLSF::InsertFreeBlock(Block* block) +{ + VMA_ASSERT(block != m_NullBlock); + VMA_ASSERT(!block->IsFree() && "Cannot insert block twice!"); + + uint8_t memClass = SizeToMemoryClass(block->size); + uint16_t secondIndex = SizeToSecondIndex(block->size, memClass); + uint32_t index = GetListIndex(memClass, secondIndex); + VMA_ASSERT(index < m_ListsCount); + block->PrevFree() = VMA_NULL; + block->NextFree() = m_FreeList[index]; + m_FreeList[index] = block; + if (block->NextFree() != VMA_NULL) + block->NextFree()->PrevFree() = block; + else + { + m_InnerIsFreeBitmap[memClass] |= 1U << secondIndex; + m_IsFreeBitmap |= 1UL << memClass; + } + ++m_BlocksFreeCount; + m_BlocksFreeSize += block->size; +} + +void VmaBlockMetadata_TLSF::MergeBlock(Block* block, Block* prev) +{ + VMA_ASSERT(block->prevPhysical == prev && "Cannot merge separate physical regions!"); + VMA_ASSERT(!prev->IsFree() && "Cannot merge block that belongs to free list!"); + + block->offset = prev->offset; + block->size += prev->size; + block->prevPhysical = prev->prevPhysical; + if (block->prevPhysical) + block->prevPhysical->nextPhysical = block; + m_BlockAllocator.Free(prev); +} + +VmaBlockMetadata_TLSF::Block* VmaBlockMetadata_TLSF::FindFreeBlock(VkDeviceSize size, uint32_t& listIndex) const +{ + uint8_t memoryClass = SizeToMemoryClass(size); + uint32_t innerFreeMap = m_InnerIsFreeBitmap[memoryClass] & (~0U << SizeToSecondIndex(size, memoryClass)); + if (!innerFreeMap) + { + // Check higher levels for available blocks + uint32_t freeMap = m_IsFreeBitmap & (~0UL << (memoryClass + 1)); + if (!freeMap) + return VMA_NULL; // No more memory available + + // Find lowest free region + memoryClass = VMA_BITSCAN_LSB(freeMap); + innerFreeMap = m_InnerIsFreeBitmap[memoryClass]; + VMA_ASSERT(innerFreeMap != 0); + } + // Find lowest free subregion + listIndex = GetListIndex(memoryClass, VMA_BITSCAN_LSB(innerFreeMap)); + VMA_ASSERT(m_FreeList[listIndex]); + return m_FreeList[listIndex]; +} + +bool VmaBlockMetadata_TLSF::CheckBlock( + Block& block, + uint32_t listIndex, + VkDeviceSize allocSize, + VkDeviceSize allocAlignment, + VmaSuballocationType allocType, + VmaAllocationRequest* pAllocationRequest) +{ + VMA_ASSERT(block.IsFree() && "Block is already taken!"); + + VkDeviceSize alignedOffset = VmaAlignUp(block.offset, allocAlignment); + if (block.size < allocSize + alignedOffset - block.offset) + return false; + + // Check for granularity conflicts + if (!IsVirtual() && + m_GranularityHandler.CheckConflictAndAlignUp(alignedOffset, allocSize, block.offset, block.size, allocType)) + return false; + + // Alloc successful + pAllocationRequest->type = VmaAllocationRequestType::TLSF; + pAllocationRequest->allocHandle = (VmaAllocHandle)█ + pAllocationRequest->size = allocSize - GetDebugMargin(); + pAllocationRequest->customData = (void*)allocType; + pAllocationRequest->algorithmData = alignedOffset; + + // Place block at the start of list if it's normal block + if (listIndex != m_ListsCount && block.PrevFree()) + { + block.PrevFree()->NextFree() = block.NextFree(); + if (block.NextFree()) + block.NextFree()->PrevFree() = block.PrevFree(); + block.PrevFree() = VMA_NULL; + block.NextFree() = m_FreeList[listIndex]; + m_FreeList[listIndex] = █ + if (block.NextFree()) + block.NextFree()->PrevFree() = █ + } + + return true; +} +#endif // _VMA_BLOCK_METADATA_TLSF_FUNCTIONS +#endif // _VMA_BLOCK_METADATA_TLSF + +#ifndef _VMA_BLOCK_VECTOR +/* +Sequence of VmaDeviceMemoryBlock. Represents memory blocks allocated for a specific +Vulkan memory type. + +Synchronized internally with a mutex. +*/ +class VmaBlockVector +{ + friend struct VmaDefragmentationContext_T; + VMA_CLASS_NO_COPY(VmaBlockVector) +public: + VmaBlockVector( + VmaAllocator hAllocator, + VmaPool hParentPool, + uint32_t memoryTypeIndex, + VkDeviceSize preferredBlockSize, + size_t minBlockCount, + size_t maxBlockCount, + VkDeviceSize bufferImageGranularity, + bool explicitBlockSize, + uint32_t algorithm, + float priority, + VkDeviceSize minAllocationAlignment, + void* pMemoryAllocateNext); + ~VmaBlockVector(); + + VmaAllocator GetAllocator() const { return m_hAllocator; } + VmaPool GetParentPool() const { return m_hParentPool; } + bool IsCustomPool() const { return m_hParentPool != VMA_NULL; } + uint32_t GetMemoryTypeIndex() const { return m_MemoryTypeIndex; } + VkDeviceSize GetPreferredBlockSize() const { return m_PreferredBlockSize; } + VkDeviceSize GetBufferImageGranularity() const { return m_BufferImageGranularity; } + uint32_t GetAlgorithm() const { return m_Algorithm; } + bool HasExplicitBlockSize() const { return m_ExplicitBlockSize; } + float GetPriority() const { return m_Priority; } + const void* GetAllocationNextPtr() const { return m_pMemoryAllocateNext; } + // To be used only while the m_Mutex is locked. Used during defragmentation. + size_t GetBlockCount() const { return m_Blocks.size(); } + // To be used only while the m_Mutex is locked. Used during defragmentation. + VmaDeviceMemoryBlock* GetBlock(size_t index) const { return m_Blocks[index]; } + VMA_RW_MUTEX &GetMutex() { return m_Mutex; } + + VkResult CreateMinBlocks(); + void AddStatistics(VmaStatistics& inoutStats); + void AddDetailedStatistics(VmaDetailedStatistics& inoutStats); + bool IsEmpty(); + bool IsCorruptionDetectionEnabled() const; + + VkResult Allocate( + VkDeviceSize size, + VkDeviceSize alignment, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + size_t allocationCount, + VmaAllocation* pAllocations); + + void Free(const VmaAllocation hAllocation); + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap(class VmaJsonWriter& json); +#endif + + VkResult CheckCorruption(); + +private: + const VmaAllocator m_hAllocator; + const VmaPool m_hParentPool; + const uint32_t m_MemoryTypeIndex; + const VkDeviceSize m_PreferredBlockSize; + const size_t m_MinBlockCount; + const size_t m_MaxBlockCount; + const VkDeviceSize m_BufferImageGranularity; + const bool m_ExplicitBlockSize; + const uint32_t m_Algorithm; + const float m_Priority; + const VkDeviceSize m_MinAllocationAlignment; + + void* const m_pMemoryAllocateNext; + VMA_RW_MUTEX m_Mutex; + // Incrementally sorted by sumFreeSize, ascending. + VmaVector> m_Blocks; + uint32_t m_NextBlockId; + bool m_IncrementalSort = true; + + void SetIncrementalSort(bool val) { m_IncrementalSort = val; } + + VkDeviceSize CalcMaxBlockSize() const; + // Finds and removes given block from vector. + void Remove(VmaDeviceMemoryBlock* pBlock); + // Performs single step in sorting m_Blocks. They may not be fully sorted + // after this call. + void IncrementallySortBlocks(); + void SortByFreeSize(); + + VkResult AllocatePage( + VkDeviceSize size, + VkDeviceSize alignment, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + VmaAllocation* pAllocation); + + VkResult AllocateFromBlock( + VmaDeviceMemoryBlock* pBlock, + VkDeviceSize size, + VkDeviceSize alignment, + VmaAllocationCreateFlags allocFlags, + void* pUserData, + VmaSuballocationType suballocType, + uint32_t strategy, + VmaAllocation* pAllocation); + + VkResult CommitAllocationRequest( + VmaAllocationRequest& allocRequest, + VmaDeviceMemoryBlock* pBlock, + VkDeviceSize alignment, + VmaAllocationCreateFlags allocFlags, + void* pUserData, + VmaSuballocationType suballocType, + VmaAllocation* pAllocation); + + VkResult CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex); + bool HasEmptyBlock(); +}; +#endif // _VMA_BLOCK_VECTOR + +#ifndef _VMA_DEFRAGMENTATION_CONTEXT +struct VmaDefragmentationContext_T +{ + VMA_CLASS_NO_COPY(VmaDefragmentationContext_T) +public: + VmaDefragmentationContext_T( + VmaAllocator hAllocator, + const VmaDefragmentationInfo& info); + ~VmaDefragmentationContext_T(); + + void GetStats(VmaDefragmentationStats& outStats) { outStats = m_GlobalStats; } + + VkResult DefragmentPassBegin(VmaDefragmentationPassMoveInfo& moveInfo); + VkResult DefragmentPassEnd(VmaDefragmentationPassMoveInfo& moveInfo); + +private: + // Max number of allocations to ignore due to size constraints before ending single pass + static const uint8_t MAX_ALLOCS_TO_IGNORE = 16; + enum class CounterStatus { Pass, Ignore, End }; + + struct FragmentedBlock + { + uint32_t data; + VmaDeviceMemoryBlock* block; + }; + struct StateBalanced + { + VkDeviceSize avgFreeSize = 0; + VkDeviceSize avgAllocSize = UINT64_MAX; + }; + struct StateExtensive + { + enum class Operation : uint8_t + { + FindFreeBlockBuffer, FindFreeBlockTexture, FindFreeBlockAll, + MoveBuffers, MoveTextures, MoveAll, + Cleanup, Done + }; + + Operation operation = Operation::FindFreeBlockTexture; + size_t firstFreeBlock = SIZE_MAX; + }; + struct MoveAllocationData + { + VkDeviceSize size; + VkDeviceSize alignment; + VmaSuballocationType type; + VmaAllocationCreateFlags flags; + VmaDefragmentationMove move = {}; + }; + + const VkDeviceSize m_MaxPassBytes; + const uint32_t m_MaxPassAllocations; + + VmaStlAllocator m_MoveAllocator; + VmaVector> m_Moves; + + uint8_t m_IgnoredAllocs = 0; + uint32_t m_Algorithm; + uint32_t m_BlockVectorCount; + VmaBlockVector* m_PoolBlockVector; + VmaBlockVector** m_pBlockVectors; + size_t m_ImmovableBlockCount = 0; + VmaDefragmentationStats m_GlobalStats = { 0 }; + VmaDefragmentationStats m_PassStats = { 0 }; + void* m_AlgorithmState = VMA_NULL; + + static MoveAllocationData GetMoveData(VmaAllocHandle handle, VmaBlockMetadata* metadata); + CounterStatus CheckCounters(VkDeviceSize bytes); + bool IncrementCounters(VkDeviceSize bytes); + bool ReallocWithinBlock(VmaBlockVector& vector, VmaDeviceMemoryBlock* block); + bool AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, VmaBlockVector& vector); + + bool ComputeDefragmentation(VmaBlockVector& vector, size_t index); + bool ComputeDefragmentation_Fast(VmaBlockVector& vector); + bool ComputeDefragmentation_Balanced(VmaBlockVector& vector, size_t index, bool update); + bool ComputeDefragmentation_Full(VmaBlockVector& vector); + bool ComputeDefragmentation_Extensive(VmaBlockVector& vector, size_t index); + + void UpdateVectorStatistics(VmaBlockVector& vector, StateBalanced& state); + bool MoveDataToFreeBlocks(VmaSuballocationType currentType, + VmaBlockVector& vector, size_t firstFreeBlock, + bool& texturePresent, bool& bufferPresent, bool& otherPresent); +}; +#endif // _VMA_DEFRAGMENTATION_CONTEXT + +#ifndef _VMA_POOL_T +struct VmaPool_T +{ + friend struct VmaPoolListItemTraits; + VMA_CLASS_NO_COPY(VmaPool_T) +public: + VmaBlockVector m_BlockVector; + VmaDedicatedAllocationList m_DedicatedAllocations; + + VmaPool_T( + VmaAllocator hAllocator, + const VmaPoolCreateInfo& createInfo, + VkDeviceSize preferredBlockSize); + ~VmaPool_T(); + + uint32_t GetId() const { return m_Id; } + void SetId(uint32_t id) { VMA_ASSERT(m_Id == 0); m_Id = id; } + + const char* GetName() const { return m_Name; } + void SetName(const char* pName); + +#if VMA_STATS_STRING_ENABLED + //void PrintDetailedMap(class VmaStringBuilder& sb); +#endif + +private: + uint32_t m_Id; + char* m_Name; + VmaPool_T* m_PrevPool = VMA_NULL; + VmaPool_T* m_NextPool = VMA_NULL; +}; + +struct VmaPoolListItemTraits +{ + typedef VmaPool_T ItemType; + + static ItemType* GetPrev(const ItemType* item) { return item->m_PrevPool; } + static ItemType* GetNext(const ItemType* item) { return item->m_NextPool; } + static ItemType*& AccessPrev(ItemType* item) { return item->m_PrevPool; } + static ItemType*& AccessNext(ItemType* item) { return item->m_NextPool; } +}; +#endif // _VMA_POOL_T + +#ifndef _VMA_CURRENT_BUDGET_DATA +struct VmaCurrentBudgetData +{ + VMA_ATOMIC_UINT32 m_BlockCount[VK_MAX_MEMORY_HEAPS]; + VMA_ATOMIC_UINT32 m_AllocationCount[VK_MAX_MEMORY_HEAPS]; + VMA_ATOMIC_UINT64 m_BlockBytes[VK_MAX_MEMORY_HEAPS]; + VMA_ATOMIC_UINT64 m_AllocationBytes[VK_MAX_MEMORY_HEAPS]; + +#if VMA_MEMORY_BUDGET + VMA_ATOMIC_UINT32 m_OperationsSinceBudgetFetch; + VMA_RW_MUTEX m_BudgetMutex; + uint64_t m_VulkanUsage[VK_MAX_MEMORY_HEAPS]; + uint64_t m_VulkanBudget[VK_MAX_MEMORY_HEAPS]; + uint64_t m_BlockBytesAtBudgetFetch[VK_MAX_MEMORY_HEAPS]; +#endif // VMA_MEMORY_BUDGET + + VmaCurrentBudgetData(); + + void AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize); + void RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize); +}; + +#ifndef _VMA_CURRENT_BUDGET_DATA_FUNCTIONS +VmaCurrentBudgetData::VmaCurrentBudgetData() +{ + for (uint32_t heapIndex = 0; heapIndex < VK_MAX_MEMORY_HEAPS; ++heapIndex) + { + m_BlockCount[heapIndex] = 0; + m_AllocationCount[heapIndex] = 0; + m_BlockBytes[heapIndex] = 0; + m_AllocationBytes[heapIndex] = 0; +#if VMA_MEMORY_BUDGET + m_VulkanUsage[heapIndex] = 0; + m_VulkanBudget[heapIndex] = 0; + m_BlockBytesAtBudgetFetch[heapIndex] = 0; +#endif + } + +#if VMA_MEMORY_BUDGET + m_OperationsSinceBudgetFetch = 0; +#endif +} + +void VmaCurrentBudgetData::AddAllocation(uint32_t heapIndex, VkDeviceSize allocationSize) +{ + m_AllocationBytes[heapIndex] += allocationSize; + ++m_AllocationCount[heapIndex]; +#if VMA_MEMORY_BUDGET + ++m_OperationsSinceBudgetFetch; +#endif +} + +void VmaCurrentBudgetData::RemoveAllocation(uint32_t heapIndex, VkDeviceSize allocationSize) +{ + VMA_ASSERT(m_AllocationBytes[heapIndex] >= allocationSize); + m_AllocationBytes[heapIndex] -= allocationSize; + VMA_ASSERT(m_AllocationCount[heapIndex] > 0); + --m_AllocationCount[heapIndex]; +#if VMA_MEMORY_BUDGET + ++m_OperationsSinceBudgetFetch; +#endif +} +#endif // _VMA_CURRENT_BUDGET_DATA_FUNCTIONS +#endif // _VMA_CURRENT_BUDGET_DATA + +#ifndef _VMA_ALLOCATION_OBJECT_ALLOCATOR +/* +Thread-safe wrapper over VmaPoolAllocator free list, for allocation of VmaAllocation_T objects. +*/ +class VmaAllocationObjectAllocator +{ + VMA_CLASS_NO_COPY(VmaAllocationObjectAllocator) +public: + VmaAllocationObjectAllocator(const VkAllocationCallbacks* pAllocationCallbacks) + : m_Allocator(pAllocationCallbacks, 1024) {} + + template VmaAllocation Allocate(Types&&... args); + void Free(VmaAllocation hAlloc); + +private: + VMA_MUTEX m_Mutex; + VmaPoolAllocator m_Allocator; +}; + +template +VmaAllocation VmaAllocationObjectAllocator::Allocate(Types&&... args) +{ + VmaMutexLock mutexLock(m_Mutex); + return m_Allocator.Alloc(std::forward(args)...); +} + +void VmaAllocationObjectAllocator::Free(VmaAllocation hAlloc) +{ + VmaMutexLock mutexLock(m_Mutex); + m_Allocator.Free(hAlloc); +} +#endif // _VMA_ALLOCATION_OBJECT_ALLOCATOR + +#ifndef _VMA_VIRTUAL_BLOCK_T +struct VmaVirtualBlock_T +{ + VMA_CLASS_NO_COPY(VmaVirtualBlock_T) +public: + const bool m_AllocationCallbacksSpecified; + const VkAllocationCallbacks m_AllocationCallbacks; + + VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo); + ~VmaVirtualBlock_T(); + + VkResult Init() { return VK_SUCCESS; } + bool IsEmpty() const { return m_Metadata->IsEmpty(); } + void Free(VmaVirtualAllocation allocation) { m_Metadata->Free((VmaAllocHandle)allocation); } + void SetAllocationUserData(VmaVirtualAllocation allocation, void* userData) { m_Metadata->SetAllocationUserData((VmaAllocHandle)allocation, userData); } + void Clear() { m_Metadata->Clear(); } + + const VkAllocationCallbacks* GetAllocationCallbacks() const; + void GetAllocationInfo(VmaVirtualAllocation allocation, VmaVirtualAllocationInfo& outInfo); + VkResult Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VmaVirtualAllocation& outAllocation, + VkDeviceSize* outOffset); + void GetStatistics(VmaStatistics& outStats) const; + void CalculateDetailedStatistics(VmaDetailedStatistics& outStats) const; +#if VMA_STATS_STRING_ENABLED + void BuildStatsString(bool detailedMap, VmaStringBuilder& sb) const; +#endif + +private: + VmaBlockMetadata* m_Metadata; +}; + +#ifndef _VMA_VIRTUAL_BLOCK_T_FUNCTIONS +VmaVirtualBlock_T::VmaVirtualBlock_T(const VmaVirtualBlockCreateInfo& createInfo) + : m_AllocationCallbacksSpecified(createInfo.pAllocationCallbacks != VMA_NULL), + m_AllocationCallbacks(createInfo.pAllocationCallbacks != VMA_NULL ? *createInfo.pAllocationCallbacks : VmaEmptyAllocationCallbacks) +{ + const uint32_t algorithm = createInfo.flags & VMA_VIRTUAL_BLOCK_CREATE_ALGORITHM_MASK; + switch (algorithm) + { + case 0: + m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_TLSF)(VK_NULL_HANDLE, 1, true); + break; + case VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT: + m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_Linear)(VK_NULL_HANDLE, 1, true); + break; + default: + VMA_ASSERT(0); + m_Metadata = vma_new(GetAllocationCallbacks(), VmaBlockMetadata_TLSF)(VK_NULL_HANDLE, 1, true); + } + + m_Metadata->Init(createInfo.size); +} + +VmaVirtualBlock_T::~VmaVirtualBlock_T() +{ + // Define macro VMA_DEBUG_LOG_FORMAT to receive the list of the unfreed allocations + if (!m_Metadata->IsEmpty()) + m_Metadata->DebugLogAllAllocations(); + // This is the most important assert in the entire library. + // Hitting it means you have some memory leak - unreleased virtual allocations. + VMA_ASSERT(m_Metadata->IsEmpty() && "Some virtual allocations were not freed before destruction of this virtual block!"); + + vma_delete(GetAllocationCallbacks(), m_Metadata); +} + +const VkAllocationCallbacks* VmaVirtualBlock_T::GetAllocationCallbacks() const +{ + return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : VMA_NULL; +} + +void VmaVirtualBlock_T::GetAllocationInfo(VmaVirtualAllocation allocation, VmaVirtualAllocationInfo& outInfo) +{ + m_Metadata->GetAllocationInfo((VmaAllocHandle)allocation, outInfo); +} + +VkResult VmaVirtualBlock_T::Allocate(const VmaVirtualAllocationCreateInfo& createInfo, VmaVirtualAllocation& outAllocation, + VkDeviceSize* outOffset) +{ + VmaAllocationRequest request = {}; + if (m_Metadata->CreateAllocationRequest( + createInfo.size, // allocSize + VMA_MAX(createInfo.alignment, (VkDeviceSize)1), // allocAlignment + (createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0, // upperAddress + VMA_SUBALLOCATION_TYPE_UNKNOWN, // allocType - unimportant + createInfo.flags & VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MASK, // strategy + &request)) + { + m_Metadata->Alloc(request, + VMA_SUBALLOCATION_TYPE_UNKNOWN, // type - unimportant + createInfo.pUserData); + outAllocation = (VmaVirtualAllocation)request.allocHandle; + if(outOffset) + *outOffset = m_Metadata->GetAllocationOffset(request.allocHandle); + return VK_SUCCESS; + } + outAllocation = (VmaVirtualAllocation)VK_NULL_HANDLE; + if (outOffset) + *outOffset = UINT64_MAX; + return VK_ERROR_OUT_OF_DEVICE_MEMORY; +} + +void VmaVirtualBlock_T::GetStatistics(VmaStatistics& outStats) const +{ + VmaClearStatistics(outStats); + m_Metadata->AddStatistics(outStats); +} + +void VmaVirtualBlock_T::CalculateDetailedStatistics(VmaDetailedStatistics& outStats) const +{ + VmaClearDetailedStatistics(outStats); + m_Metadata->AddDetailedStatistics(outStats); +} + +#if VMA_STATS_STRING_ENABLED +void VmaVirtualBlock_T::BuildStatsString(bool detailedMap, VmaStringBuilder& sb) const +{ + VmaJsonWriter json(GetAllocationCallbacks(), sb); + json.BeginObject(); + + VmaDetailedStatistics stats; + CalculateDetailedStatistics(stats); + + json.WriteString("Stats"); + VmaPrintDetailedStatistics(json, stats); + + if (detailedMap) + { + json.WriteString("Details"); + json.BeginObject(); + m_Metadata->PrintDetailedMap(json); + json.EndObject(); + } + + json.EndObject(); +} +#endif // VMA_STATS_STRING_ENABLED +#endif // _VMA_VIRTUAL_BLOCK_T_FUNCTIONS +#endif // _VMA_VIRTUAL_BLOCK_T + + +// Main allocator object. +struct VmaAllocator_T +{ + VMA_CLASS_NO_COPY(VmaAllocator_T) +public: + bool m_UseMutex; + uint32_t m_VulkanApiVersion; + bool m_UseKhrDedicatedAllocation; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0). + bool m_UseKhrBindMemory2; // Can be set only if m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0). + bool m_UseExtMemoryBudget; + bool m_UseAmdDeviceCoherentMemory; + bool m_UseKhrBufferDeviceAddress; + bool m_UseExtMemoryPriority; + VkDevice m_hDevice; + VkInstance m_hInstance; + bool m_AllocationCallbacksSpecified; + VkAllocationCallbacks m_AllocationCallbacks; + VmaDeviceMemoryCallbacks m_DeviceMemoryCallbacks; + VmaAllocationObjectAllocator m_AllocationObjectAllocator; + + // Each bit (1 << i) is set if HeapSizeLimit is enabled for that heap, so cannot allocate more than the heap size. + uint32_t m_HeapSizeLimitMask; + + VkPhysicalDeviceProperties m_PhysicalDeviceProperties; + VkPhysicalDeviceMemoryProperties m_MemProps; + + // Default pools. + VmaBlockVector* m_pBlockVectors[VK_MAX_MEMORY_TYPES]; + VmaDedicatedAllocationList m_DedicatedAllocations[VK_MAX_MEMORY_TYPES]; + + VmaCurrentBudgetData m_Budget; + VMA_ATOMIC_UINT32 m_DeviceMemoryCount; // Total number of VkDeviceMemory objects. + + VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo); + VkResult Init(const VmaAllocatorCreateInfo* pCreateInfo); + ~VmaAllocator_T(); + + const VkAllocationCallbacks* GetAllocationCallbacks() const + { + return m_AllocationCallbacksSpecified ? &m_AllocationCallbacks : VMA_NULL; + } + const VmaVulkanFunctions& GetVulkanFunctions() const + { + return m_VulkanFunctions; + } + + VkPhysicalDevice GetPhysicalDevice() const { return m_PhysicalDevice; } + + VkDeviceSize GetBufferImageGranularity() const + { + return VMA_MAX( + static_cast(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY), + m_PhysicalDeviceProperties.limits.bufferImageGranularity); + } + + uint32_t GetMemoryHeapCount() const { return m_MemProps.memoryHeapCount; } + uint32_t GetMemoryTypeCount() const { return m_MemProps.memoryTypeCount; } + + uint32_t MemoryTypeIndexToHeapIndex(uint32_t memTypeIndex) const + { + VMA_ASSERT(memTypeIndex < m_MemProps.memoryTypeCount); + return m_MemProps.memoryTypes[memTypeIndex].heapIndex; + } + // True when specific memory type is HOST_VISIBLE but not HOST_COHERENT. + bool IsMemoryTypeNonCoherent(uint32_t memTypeIndex) const + { + return (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & (VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT)) == + VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; + } + // Minimum alignment for all allocations in specific memory type. + VkDeviceSize GetMemoryTypeMinAlignment(uint32_t memTypeIndex) const + { + return IsMemoryTypeNonCoherent(memTypeIndex) ? + VMA_MAX((VkDeviceSize)VMA_MIN_ALIGNMENT, m_PhysicalDeviceProperties.limits.nonCoherentAtomSize) : + (VkDeviceSize)VMA_MIN_ALIGNMENT; + } + + bool IsIntegratedGpu() const + { + return m_PhysicalDeviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU; + } + + uint32_t GetGlobalMemoryTypeBits() const { return m_GlobalMemoryTypeBits; } + + void GetBufferMemoryRequirements( + VkBuffer hBuffer, + VkMemoryRequirements& memReq, + bool& requiresDedicatedAllocation, + bool& prefersDedicatedAllocation) const; + void GetImageMemoryRequirements( + VkImage hImage, + VkMemoryRequirements& memReq, + bool& requiresDedicatedAllocation, + bool& prefersDedicatedAllocation) const; + VkResult FindMemoryTypeIndex( + uint32_t memoryTypeBits, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + VkFlags bufImgUsage, // VkBufferCreateInfo::usage or VkImageCreateInfo::usage. UINT32_MAX if unknown. + uint32_t* pMemoryTypeIndex) const; + + // Main allocation function. + VkResult AllocateMemory( + const VkMemoryRequirements& vkMemReq, + bool requiresDedicatedAllocation, + bool prefersDedicatedAllocation, + VkBuffer dedicatedBuffer, + VkImage dedicatedImage, + VkFlags dedicatedBufferImageUsage, // UINT32_MAX if unknown. + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + size_t allocationCount, + VmaAllocation* pAllocations); + + // Main deallocation function. + void FreeMemory( + size_t allocationCount, + const VmaAllocation* pAllocations); + + void CalculateStatistics(VmaTotalStatistics* pStats); + + void GetHeapBudgets( + VmaBudget* outBudgets, uint32_t firstHeap, uint32_t heapCount); + +#if VMA_STATS_STRING_ENABLED + void PrintDetailedMap(class VmaJsonWriter& json); +#endif + + void GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo); + + VkResult CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool); + void DestroyPool(VmaPool pool); + void GetPoolStatistics(VmaPool pool, VmaStatistics* pPoolStats); + void CalculatePoolStatistics(VmaPool pool, VmaDetailedStatistics* pPoolStats); + + void SetCurrentFrameIndex(uint32_t frameIndex); + uint32_t GetCurrentFrameIndex() const { return m_CurrentFrameIndex.load(); } + + VkResult CheckPoolCorruption(VmaPool hPool); + VkResult CheckCorruption(uint32_t memoryTypeBits); + + // Call to Vulkan function vkAllocateMemory with accompanying bookkeeping. + VkResult AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory); + // Call to Vulkan function vkFreeMemory with accompanying bookkeeping. + void FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory); + // Call to Vulkan function vkBindBufferMemory or vkBindBufferMemory2KHR. + VkResult BindVulkanBuffer( + VkDeviceMemory memory, + VkDeviceSize memoryOffset, + VkBuffer buffer, + const void* pNext); + // Call to Vulkan function vkBindImageMemory or vkBindImageMemory2KHR. + VkResult BindVulkanImage( + VkDeviceMemory memory, + VkDeviceSize memoryOffset, + VkImage image, + const void* pNext); + + VkResult Map(VmaAllocation hAllocation, void** ppData); + void Unmap(VmaAllocation hAllocation); + + VkResult BindBufferMemory( + VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkBuffer hBuffer, + const void* pNext); + VkResult BindImageMemory( + VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkImage hImage, + const void* pNext); + + VkResult FlushOrInvalidateAllocation( + VmaAllocation hAllocation, + VkDeviceSize offset, VkDeviceSize size, + VMA_CACHE_OPERATION op); + VkResult FlushOrInvalidateAllocations( + uint32_t allocationCount, + const VmaAllocation* allocations, + const VkDeviceSize* offsets, const VkDeviceSize* sizes, + VMA_CACHE_OPERATION op); + + void FillAllocation(const VmaAllocation hAllocation, uint8_t pattern); + + /* + Returns bit mask of memory types that can support defragmentation on GPU as + they support creation of required buffer for copy operations. + */ + uint32_t GetGpuDefragmentationMemoryTypeBits(); + +#if VMA_EXTERNAL_MEMORY + VkExternalMemoryHandleTypeFlagsKHR GetExternalMemoryHandleTypeFlags(uint32_t memTypeIndex) const + { + return m_TypeExternalMemoryHandleTypes[memTypeIndex]; + } +#endif // #if VMA_EXTERNAL_MEMORY + +private: + VkDeviceSize m_PreferredLargeHeapBlockSize; + + VkPhysicalDevice m_PhysicalDevice; + VMA_ATOMIC_UINT32 m_CurrentFrameIndex; + VMA_ATOMIC_UINT32 m_GpuDefragmentationMemoryTypeBits; // UINT32_MAX means uninitialized. +#if VMA_EXTERNAL_MEMORY + VkExternalMemoryHandleTypeFlagsKHR m_TypeExternalMemoryHandleTypes[VK_MAX_MEMORY_TYPES]; +#endif // #if VMA_EXTERNAL_MEMORY + + VMA_RW_MUTEX m_PoolsMutex; + typedef VmaIntrusiveLinkedList PoolList; + // Protected by m_PoolsMutex. + PoolList m_Pools; + uint32_t m_NextPoolId; + + VmaVulkanFunctions m_VulkanFunctions; + + // Global bit mask AND-ed with any memoryTypeBits to disallow certain memory types. + uint32_t m_GlobalMemoryTypeBits; + + void ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions); + +#if VMA_STATIC_VULKAN_FUNCTIONS == 1 + void ImportVulkanFunctions_Static(); +#endif + + void ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions); + +#if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1 + void ImportVulkanFunctions_Dynamic(); +#endif + + void ValidateVulkanFunctions(); + + VkDeviceSize CalcPreferredBlockSize(uint32_t memTypeIndex); + + VkResult AllocateMemoryOfType( + VmaPool pool, + VkDeviceSize size, + VkDeviceSize alignment, + bool dedicatedPreferred, + VkBuffer dedicatedBuffer, + VkImage dedicatedImage, + VkFlags dedicatedBufferImageUsage, + const VmaAllocationCreateInfo& createInfo, + uint32_t memTypeIndex, + VmaSuballocationType suballocType, + VmaDedicatedAllocationList& dedicatedAllocations, + VmaBlockVector& blockVector, + size_t allocationCount, + VmaAllocation* pAllocations); + + // Helper function only to be used inside AllocateDedicatedMemory. + VkResult AllocateDedicatedMemoryPage( + VmaPool pool, + VkDeviceSize size, + VmaSuballocationType suballocType, + uint32_t memTypeIndex, + const VkMemoryAllocateInfo& allocInfo, + bool map, + bool isUserDataString, + bool isMappingAllowed, + void* pUserData, + VmaAllocation* pAllocation); + + // Allocates and registers new VkDeviceMemory specifically for dedicated allocations. + VkResult AllocateDedicatedMemory( + VmaPool pool, + VkDeviceSize size, + VmaSuballocationType suballocType, + VmaDedicatedAllocationList& dedicatedAllocations, + uint32_t memTypeIndex, + bool map, + bool isUserDataString, + bool isMappingAllowed, + bool canAliasMemory, + void* pUserData, + float priority, + VkBuffer dedicatedBuffer, + VkImage dedicatedImage, + VkFlags dedicatedBufferImageUsage, + size_t allocationCount, + VmaAllocation* pAllocations, + const void* pNextChain = nullptr); + + void FreeDedicatedMemory(const VmaAllocation allocation); + + VkResult CalcMemTypeParams( + VmaAllocationCreateInfo& outCreateInfo, + uint32_t memTypeIndex, + VkDeviceSize size, + size_t allocationCount); + VkResult CalcAllocationParams( + VmaAllocationCreateInfo& outCreateInfo, + bool dedicatedRequired, + bool dedicatedPreferred); + + /* + Calculates and returns bit mask of memory types that can support defragmentation + on GPU as they support creation of required buffer for copy operations. + */ + uint32_t CalculateGpuDefragmentationMemoryTypeBits() const; + uint32_t CalculateGlobalMemoryTypeBits() const; + + bool GetFlushOrInvalidateRange( + VmaAllocation allocation, + VkDeviceSize offset, VkDeviceSize size, + VkMappedMemoryRange& outRange) const; + +#if VMA_MEMORY_BUDGET + void UpdateVulkanBudget(); +#endif // #if VMA_MEMORY_BUDGET +}; + + +#ifndef _VMA_MEMORY_FUNCTIONS +static void* VmaMalloc(VmaAllocator hAllocator, size_t size, size_t alignment) +{ + return VmaMalloc(&hAllocator->m_AllocationCallbacks, size, alignment); +} + +static void VmaFree(VmaAllocator hAllocator, void* ptr) +{ + VmaFree(&hAllocator->m_AllocationCallbacks, ptr); +} + +template +static T* VmaAllocate(VmaAllocator hAllocator) +{ + return (T*)VmaMalloc(hAllocator, sizeof(T), VMA_ALIGN_OF(T)); +} + +template +static T* VmaAllocateArray(VmaAllocator hAllocator, size_t count) +{ + return (T*)VmaMalloc(hAllocator, sizeof(T) * count, VMA_ALIGN_OF(T)); +} + +template +static void vma_delete(VmaAllocator hAllocator, T* ptr) +{ + if(ptr != VMA_NULL) + { + ptr->~T(); + VmaFree(hAllocator, ptr); + } +} + +template +static void vma_delete_array(VmaAllocator hAllocator, T* ptr, size_t count) +{ + if(ptr != VMA_NULL) + { + for(size_t i = count; i--; ) + ptr[i].~T(); + VmaFree(hAllocator, ptr); + } +} +#endif // _VMA_MEMORY_FUNCTIONS + +#ifndef _VMA_DEVICE_MEMORY_BLOCK_FUNCTIONS +VmaDeviceMemoryBlock::VmaDeviceMemoryBlock(VmaAllocator hAllocator) + : m_pMetadata(VMA_NULL), + m_MemoryTypeIndex(UINT32_MAX), + m_Id(0), + m_hMemory(VK_NULL_HANDLE), + m_MapCount(0), + m_pMappedData(VMA_NULL) {} + +VmaDeviceMemoryBlock::~VmaDeviceMemoryBlock() +{ + VMA_ASSERT(m_MapCount == 0 && "VkDeviceMemory block is being destroyed while it is still mapped."); + VMA_ASSERT(m_hMemory == VK_NULL_HANDLE); +} + +void VmaDeviceMemoryBlock::Init( + VmaAllocator hAllocator, + VmaPool hParentPool, + uint32_t newMemoryTypeIndex, + VkDeviceMemory newMemory, + VkDeviceSize newSize, + uint32_t id, + uint32_t algorithm, + VkDeviceSize bufferImageGranularity) +{ + VMA_ASSERT(m_hMemory == VK_NULL_HANDLE); + + m_hParentPool = hParentPool; + m_MemoryTypeIndex = newMemoryTypeIndex; + m_Id = id; + m_hMemory = newMemory; + + switch (algorithm) + { + case 0: + m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_TLSF)(hAllocator->GetAllocationCallbacks(), + bufferImageGranularity, false); // isVirtual + break; + case VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT: + m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_Linear)(hAllocator->GetAllocationCallbacks(), + bufferImageGranularity, false); // isVirtual + break; + default: + VMA_ASSERT(0); + m_pMetadata = vma_new(hAllocator, VmaBlockMetadata_TLSF)(hAllocator->GetAllocationCallbacks(), + bufferImageGranularity, false); // isVirtual + } + m_pMetadata->Init(newSize); +} + +void VmaDeviceMemoryBlock::Destroy(VmaAllocator allocator) +{ + // Define macro VMA_DEBUG_LOG_FORMAT to receive the list of the unfreed allocations + if (!m_pMetadata->IsEmpty()) + m_pMetadata->DebugLogAllAllocations(); + // This is the most important assert in the entire library. + // Hitting it means you have some memory leak - unreleased VmaAllocation objects. + VMA_ASSERT(m_pMetadata->IsEmpty() && "Some allocations were not freed before destruction of this memory block!"); + + VMA_ASSERT(m_hMemory != VK_NULL_HANDLE); + allocator->FreeVulkanMemory(m_MemoryTypeIndex, m_pMetadata->GetSize(), m_hMemory); + m_hMemory = VK_NULL_HANDLE; + + vma_delete(allocator, m_pMetadata); + m_pMetadata = VMA_NULL; +} + +void VmaDeviceMemoryBlock::PostAlloc(VmaAllocator hAllocator) +{ + VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex); + m_MappingHysteresis.PostAlloc(); +} + +void VmaDeviceMemoryBlock::PostFree(VmaAllocator hAllocator) +{ + VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex); + if(m_MappingHysteresis.PostFree()) + { + VMA_ASSERT(m_MappingHysteresis.GetExtraMapping() == 0); + if (m_MapCount == 0) + { + m_pMappedData = VMA_NULL; + (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory); + } + } +} + +bool VmaDeviceMemoryBlock::Validate() const +{ + VMA_VALIDATE((m_hMemory != VK_NULL_HANDLE) && + (m_pMetadata->GetSize() != 0)); + + return m_pMetadata->Validate(); +} + +VkResult VmaDeviceMemoryBlock::CheckCorruption(VmaAllocator hAllocator) +{ + void* pData = nullptr; + VkResult res = Map(hAllocator, 1, &pData); + if (res != VK_SUCCESS) + { + return res; + } + + res = m_pMetadata->CheckCorruption(pData); + + Unmap(hAllocator, 1); + + return res; +} + +VkResult VmaDeviceMemoryBlock::Map(VmaAllocator hAllocator, uint32_t count, void** ppData) +{ + if (count == 0) + { + return VK_SUCCESS; + } + + VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex); + const uint32_t oldTotalMapCount = m_MapCount + m_MappingHysteresis.GetExtraMapping(); + m_MappingHysteresis.PostMap(); + if (oldTotalMapCount != 0) + { + m_MapCount += count; + VMA_ASSERT(m_pMappedData != VMA_NULL); + if (ppData != VMA_NULL) + { + *ppData = m_pMappedData; + } + return VK_SUCCESS; + } + else + { + VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)( + hAllocator->m_hDevice, + m_hMemory, + 0, // offset + VK_WHOLE_SIZE, + 0, // flags + &m_pMappedData); + if (result == VK_SUCCESS) + { + if (ppData != VMA_NULL) + { + *ppData = m_pMappedData; + } + m_MapCount = count; + } + return result; + } +} + +void VmaDeviceMemoryBlock::Unmap(VmaAllocator hAllocator, uint32_t count) +{ + if (count == 0) + { + return; + } + + VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex); + if (m_MapCount >= count) + { + m_MapCount -= count; + const uint32_t totalMapCount = m_MapCount + m_MappingHysteresis.GetExtraMapping(); + if (totalMapCount == 0) + { + m_pMappedData = VMA_NULL; + (*hAllocator->GetVulkanFunctions().vkUnmapMemory)(hAllocator->m_hDevice, m_hMemory); + } + m_MappingHysteresis.PostUnmap(); + } + else + { + VMA_ASSERT(0 && "VkDeviceMemory block is being unmapped while it was not previously mapped."); + } +} + +VkResult VmaDeviceMemoryBlock::WriteMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize) +{ + VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION); + + void* pData; + VkResult res = Map(hAllocator, 1, &pData); + if (res != VK_SUCCESS) + { + return res; + } + + VmaWriteMagicValue(pData, allocOffset + allocSize); + + Unmap(hAllocator, 1); + return VK_SUCCESS; +} + +VkResult VmaDeviceMemoryBlock::ValidateMagicValueAfterAllocation(VmaAllocator hAllocator, VkDeviceSize allocOffset, VkDeviceSize allocSize) +{ + VMA_ASSERT(VMA_DEBUG_MARGIN > 0 && VMA_DEBUG_MARGIN % 4 == 0 && VMA_DEBUG_DETECT_CORRUPTION); + + void* pData; + VkResult res = Map(hAllocator, 1, &pData); + if (res != VK_SUCCESS) + { + return res; + } + + if (!VmaValidateMagicValue(pData, allocOffset + allocSize)) + { + VMA_ASSERT(0 && "MEMORY CORRUPTION DETECTED AFTER FREED ALLOCATION!"); + } + + Unmap(hAllocator, 1); + return VK_SUCCESS; +} + +VkResult VmaDeviceMemoryBlock::BindBufferMemory( + const VmaAllocator hAllocator, + const VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkBuffer hBuffer, + const void* pNext) +{ + VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK && + hAllocation->GetBlock() == this); + VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() && + "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?"); + const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset; + // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads. + VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex); + return hAllocator->BindVulkanBuffer(m_hMemory, memoryOffset, hBuffer, pNext); +} + +VkResult VmaDeviceMemoryBlock::BindImageMemory( + const VmaAllocator hAllocator, + const VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkImage hImage, + const void* pNext) +{ + VMA_ASSERT(hAllocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_BLOCK && + hAllocation->GetBlock() == this); + VMA_ASSERT(allocationLocalOffset < hAllocation->GetSize() && + "Invalid allocationLocalOffset. Did you forget that this offset is relative to the beginning of the allocation, not the whole memory block?"); + const VkDeviceSize memoryOffset = hAllocation->GetOffset() + allocationLocalOffset; + // This lock is important so that we don't call vkBind... and/or vkMap... simultaneously on the same VkDeviceMemory from multiple threads. + VmaMutexLock lock(m_MapAndBindMutex, hAllocator->m_UseMutex); + return hAllocator->BindVulkanImage(m_hMemory, memoryOffset, hImage, pNext); +} +#endif // _VMA_DEVICE_MEMORY_BLOCK_FUNCTIONS + +#ifndef _VMA_ALLOCATION_T_FUNCTIONS +VmaAllocation_T::VmaAllocation_T(bool mappingAllowed) + : m_Alignment{ 1 }, + m_Size{ 0 }, + m_pUserData{ VMA_NULL }, + m_pName{ VMA_NULL }, + m_MemoryTypeIndex{ 0 }, + m_Type{ (uint8_t)ALLOCATION_TYPE_NONE }, + m_SuballocationType{ (uint8_t)VMA_SUBALLOCATION_TYPE_UNKNOWN }, + m_MapCount{ 0 }, + m_Flags{ 0 } +{ + if(mappingAllowed) + m_Flags |= (uint8_t)FLAG_MAPPING_ALLOWED; + +#if VMA_STATS_STRING_ENABLED + m_BufferImageUsage = 0; +#endif +} + +VmaAllocation_T::~VmaAllocation_T() +{ + VMA_ASSERT(m_MapCount == 0 && "Allocation was not unmapped before destruction."); + + // Check if owned string was freed. + VMA_ASSERT(m_pName == VMA_NULL); +} + +void VmaAllocation_T::InitBlockAllocation( + VmaDeviceMemoryBlock* block, + VmaAllocHandle allocHandle, + VkDeviceSize alignment, + VkDeviceSize size, + uint32_t memoryTypeIndex, + VmaSuballocationType suballocationType, + bool mapped) +{ + VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE); + VMA_ASSERT(block != VMA_NULL); + m_Type = (uint8_t)ALLOCATION_TYPE_BLOCK; + m_Alignment = alignment; + m_Size = size; + m_MemoryTypeIndex = memoryTypeIndex; + if(mapped) + { + VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it."); + m_Flags |= (uint8_t)FLAG_PERSISTENT_MAP; + } + m_SuballocationType = (uint8_t)suballocationType; + m_BlockAllocation.m_Block = block; + m_BlockAllocation.m_AllocHandle = allocHandle; +} + +void VmaAllocation_T::InitDedicatedAllocation( + VmaPool hParentPool, + uint32_t memoryTypeIndex, + VkDeviceMemory hMemory, + VmaSuballocationType suballocationType, + void* pMappedData, + VkDeviceSize size) +{ + VMA_ASSERT(m_Type == ALLOCATION_TYPE_NONE); + VMA_ASSERT(hMemory != VK_NULL_HANDLE); + m_Type = (uint8_t)ALLOCATION_TYPE_DEDICATED; + m_Alignment = 0; + m_Size = size; + m_MemoryTypeIndex = memoryTypeIndex; + m_SuballocationType = (uint8_t)suballocationType; + if(pMappedData != VMA_NULL) + { + VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it."); + m_Flags |= (uint8_t)FLAG_PERSISTENT_MAP; + } + m_DedicatedAllocation.m_hParentPool = hParentPool; + m_DedicatedAllocation.m_hMemory = hMemory; + m_DedicatedAllocation.m_pMappedData = pMappedData; + m_DedicatedAllocation.m_Prev = VMA_NULL; + m_DedicatedAllocation.m_Next = VMA_NULL; +} + +void VmaAllocation_T::SetName(VmaAllocator hAllocator, const char* pName) +{ + VMA_ASSERT(pName == VMA_NULL || pName != m_pName); + + FreeName(hAllocator); + + if (pName != VMA_NULL) + m_pName = VmaCreateStringCopy(hAllocator->GetAllocationCallbacks(), pName); +} + +uint8_t VmaAllocation_T::SwapBlockAllocation(VmaAllocator hAllocator, VmaAllocation allocation) +{ + VMA_ASSERT(allocation != VMA_NULL); + VMA_ASSERT(m_Type == ALLOCATION_TYPE_BLOCK); + VMA_ASSERT(allocation->m_Type == ALLOCATION_TYPE_BLOCK); + + if (m_MapCount != 0) + m_BlockAllocation.m_Block->Unmap(hAllocator, m_MapCount); + + m_BlockAllocation.m_Block->m_pMetadata->SetAllocationUserData(m_BlockAllocation.m_AllocHandle, allocation); + VMA_SWAP(m_BlockAllocation, allocation->m_BlockAllocation); + m_BlockAllocation.m_Block->m_pMetadata->SetAllocationUserData(m_BlockAllocation.m_AllocHandle, this); + +#if VMA_STATS_STRING_ENABLED + VMA_SWAP(m_BufferImageUsage, allocation->m_BufferImageUsage); +#endif + return m_MapCount; +} + +VmaAllocHandle VmaAllocation_T::GetAllocHandle() const +{ + switch (m_Type) + { + case ALLOCATION_TYPE_BLOCK: + return m_BlockAllocation.m_AllocHandle; + case ALLOCATION_TYPE_DEDICATED: + return VK_NULL_HANDLE; + default: + VMA_ASSERT(0); + return VK_NULL_HANDLE; + } +} + +VkDeviceSize VmaAllocation_T::GetOffset() const +{ + switch (m_Type) + { + case ALLOCATION_TYPE_BLOCK: + return m_BlockAllocation.m_Block->m_pMetadata->GetAllocationOffset(m_BlockAllocation.m_AllocHandle); + case ALLOCATION_TYPE_DEDICATED: + return 0; + default: + VMA_ASSERT(0); + return 0; + } +} + +VmaPool VmaAllocation_T::GetParentPool() const +{ + switch (m_Type) + { + case ALLOCATION_TYPE_BLOCK: + return m_BlockAllocation.m_Block->GetParentPool(); + case ALLOCATION_TYPE_DEDICATED: + return m_DedicatedAllocation.m_hParentPool; + default: + VMA_ASSERT(0); + return VK_NULL_HANDLE; + } +} + +VkDeviceMemory VmaAllocation_T::GetMemory() const +{ + switch (m_Type) + { + case ALLOCATION_TYPE_BLOCK: + return m_BlockAllocation.m_Block->GetDeviceMemory(); + case ALLOCATION_TYPE_DEDICATED: + return m_DedicatedAllocation.m_hMemory; + default: + VMA_ASSERT(0); + return VK_NULL_HANDLE; + } +} + +void* VmaAllocation_T::GetMappedData() const +{ + switch (m_Type) + { + case ALLOCATION_TYPE_BLOCK: + if (m_MapCount != 0 || IsPersistentMap()) + { + void* pBlockData = m_BlockAllocation.m_Block->GetMappedData(); + VMA_ASSERT(pBlockData != VMA_NULL); + return (char*)pBlockData + GetOffset(); + } + else + { + return VMA_NULL; + } + break; + case ALLOCATION_TYPE_DEDICATED: + VMA_ASSERT((m_DedicatedAllocation.m_pMappedData != VMA_NULL) == (m_MapCount != 0 || IsPersistentMap())); + return m_DedicatedAllocation.m_pMappedData; + default: + VMA_ASSERT(0); + return VMA_NULL; + } +} + +void VmaAllocation_T::BlockAllocMap() +{ + VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK); + VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it."); + + if (m_MapCount < 0xFF) + { + ++m_MapCount; + } + else + { + VMA_ASSERT(0 && "Allocation mapped too many times simultaneously."); + } +} + +void VmaAllocation_T::BlockAllocUnmap() +{ + VMA_ASSERT(GetType() == ALLOCATION_TYPE_BLOCK); + + if (m_MapCount > 0) + { + --m_MapCount; + } + else + { + VMA_ASSERT(0 && "Unmapping allocation not previously mapped."); + } +} + +VkResult VmaAllocation_T::DedicatedAllocMap(VmaAllocator hAllocator, void** ppData) +{ + VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED); + VMA_ASSERT(IsMappingAllowed() && "Mapping is not allowed on this allocation! Please use one of the new VMA_ALLOCATION_CREATE_HOST_ACCESS_* flags when creating it."); + + if (m_MapCount != 0 || IsPersistentMap()) + { + if (m_MapCount < 0xFF) + { + VMA_ASSERT(m_DedicatedAllocation.m_pMappedData != VMA_NULL); + *ppData = m_DedicatedAllocation.m_pMappedData; + ++m_MapCount; + return VK_SUCCESS; + } + else + { + VMA_ASSERT(0 && "Dedicated allocation mapped too many times simultaneously."); + return VK_ERROR_MEMORY_MAP_FAILED; + } + } + else + { + VkResult result = (*hAllocator->GetVulkanFunctions().vkMapMemory)( + hAllocator->m_hDevice, + m_DedicatedAllocation.m_hMemory, + 0, // offset + VK_WHOLE_SIZE, + 0, // flags + ppData); + if (result == VK_SUCCESS) + { + m_DedicatedAllocation.m_pMappedData = *ppData; + m_MapCount = 1; + } + return result; + } +} + +void VmaAllocation_T::DedicatedAllocUnmap(VmaAllocator hAllocator) +{ + VMA_ASSERT(GetType() == ALLOCATION_TYPE_DEDICATED); + + if (m_MapCount > 0) + { + --m_MapCount; + if (m_MapCount == 0 && !IsPersistentMap()) + { + m_DedicatedAllocation.m_pMappedData = VMA_NULL; + (*hAllocator->GetVulkanFunctions().vkUnmapMemory)( + hAllocator->m_hDevice, + m_DedicatedAllocation.m_hMemory); + } + } + else + { + VMA_ASSERT(0 && "Unmapping dedicated allocation not previously mapped."); + } +} + +#if VMA_STATS_STRING_ENABLED +void VmaAllocation_T::InitBufferImageUsage(uint32_t bufferImageUsage) +{ + VMA_ASSERT(m_BufferImageUsage == 0); + m_BufferImageUsage = bufferImageUsage; +} + +void VmaAllocation_T::PrintParameters(class VmaJsonWriter& json) const +{ + json.WriteString("Type"); + json.WriteString(VMA_SUBALLOCATION_TYPE_NAMES[m_SuballocationType]); + + json.WriteString("Size"); + json.WriteNumber(m_Size); + json.WriteString("Usage"); + json.WriteNumber(m_BufferImageUsage); + + if (m_pUserData != VMA_NULL) + { + json.WriteString("CustomData"); + json.BeginString(); + json.ContinueString_Pointer(m_pUserData); + json.EndString(); + } + if (m_pName != VMA_NULL) + { + json.WriteString("Name"); + json.WriteString(m_pName); + } +} +#endif // VMA_STATS_STRING_ENABLED + +void VmaAllocation_T::FreeName(VmaAllocator hAllocator) +{ + if(m_pName) + { + VmaFreeString(hAllocator->GetAllocationCallbacks(), m_pName); + m_pName = VMA_NULL; + } +} +#endif // _VMA_ALLOCATION_T_FUNCTIONS + +#ifndef _VMA_BLOCK_VECTOR_FUNCTIONS +VmaBlockVector::VmaBlockVector( + VmaAllocator hAllocator, + VmaPool hParentPool, + uint32_t memoryTypeIndex, + VkDeviceSize preferredBlockSize, + size_t minBlockCount, + size_t maxBlockCount, + VkDeviceSize bufferImageGranularity, + bool explicitBlockSize, + uint32_t algorithm, + float priority, + VkDeviceSize minAllocationAlignment, + void* pMemoryAllocateNext) + : m_hAllocator(hAllocator), + m_hParentPool(hParentPool), + m_MemoryTypeIndex(memoryTypeIndex), + m_PreferredBlockSize(preferredBlockSize), + m_MinBlockCount(minBlockCount), + m_MaxBlockCount(maxBlockCount), + m_BufferImageGranularity(bufferImageGranularity), + m_ExplicitBlockSize(explicitBlockSize), + m_Algorithm(algorithm), + m_Priority(priority), + m_MinAllocationAlignment(minAllocationAlignment), + m_pMemoryAllocateNext(pMemoryAllocateNext), + m_Blocks(VmaStlAllocator(hAllocator->GetAllocationCallbacks())), + m_NextBlockId(0) {} + +VmaBlockVector::~VmaBlockVector() +{ + for (size_t i = m_Blocks.size(); i--; ) + { + m_Blocks[i]->Destroy(m_hAllocator); + vma_delete(m_hAllocator, m_Blocks[i]); + } +} + +VkResult VmaBlockVector::CreateMinBlocks() +{ + for (size_t i = 0; i < m_MinBlockCount; ++i) + { + VkResult res = CreateBlock(m_PreferredBlockSize, VMA_NULL); + if (res != VK_SUCCESS) + { + return res; + } + } + return VK_SUCCESS; +} + +void VmaBlockVector::AddStatistics(VmaStatistics& inoutStats) +{ + VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); + + const size_t blockCount = m_Blocks.size(); + for (uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) + { + const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pBlock); + VMA_HEAVY_ASSERT(pBlock->Validate()); + pBlock->m_pMetadata->AddStatistics(inoutStats); + } +} + +void VmaBlockVector::AddDetailedStatistics(VmaDetailedStatistics& inoutStats) +{ + VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); + + const size_t blockCount = m_Blocks.size(); + for (uint32_t blockIndex = 0; blockIndex < blockCount; ++blockIndex) + { + const VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pBlock); + VMA_HEAVY_ASSERT(pBlock->Validate()); + pBlock->m_pMetadata->AddDetailedStatistics(inoutStats); + } +} + +bool VmaBlockVector::IsEmpty() +{ + VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); + return m_Blocks.empty(); +} + +bool VmaBlockVector::IsCorruptionDetectionEnabled() const +{ + const uint32_t requiredMemFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + return (VMA_DEBUG_DETECT_CORRUPTION != 0) && + (VMA_DEBUG_MARGIN > 0) && + (m_Algorithm == 0 || m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) && + (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & requiredMemFlags) == requiredMemFlags; +} + +VkResult VmaBlockVector::Allocate( + VkDeviceSize size, + VkDeviceSize alignment, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + size_t allocationCount, + VmaAllocation* pAllocations) +{ + size_t allocIndex; + VkResult res = VK_SUCCESS; + + alignment = VMA_MAX(alignment, m_MinAllocationAlignment); + + if (IsCorruptionDetectionEnabled()) + { + size = VmaAlignUp(size, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE)); + alignment = VmaAlignUp(alignment, sizeof(VMA_CORRUPTION_DETECTION_MAGIC_VALUE)); + } + + { + VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex); + for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex) + { + res = AllocatePage( + size, + alignment, + createInfo, + suballocType, + pAllocations + allocIndex); + if (res != VK_SUCCESS) + { + break; + } + } + } + + if (res != VK_SUCCESS) + { + // Free all already created allocations. + while (allocIndex--) + Free(pAllocations[allocIndex]); + memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount); + } + + return res; +} + +VkResult VmaBlockVector::AllocatePage( + VkDeviceSize size, + VkDeviceSize alignment, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + VmaAllocation* pAllocation) +{ + const bool isUpperAddress = (createInfo.flags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0; + + VkDeviceSize freeMemory; + { + const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex); + VmaBudget heapBudget = {}; + m_hAllocator->GetHeapBudgets(&heapBudget, heapIndex, 1); + freeMemory = (heapBudget.usage < heapBudget.budget) ? (heapBudget.budget - heapBudget.usage) : 0; + } + + const bool canFallbackToDedicated = !HasExplicitBlockSize() && + (createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0; + const bool canCreateNewBlock = + ((createInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0) && + (m_Blocks.size() < m_MaxBlockCount) && + (freeMemory >= size || !canFallbackToDedicated); + uint32_t strategy = createInfo.flags & VMA_ALLOCATION_CREATE_STRATEGY_MASK; + + // Upper address can only be used with linear allocator and within single memory block. + if (isUpperAddress && + (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT || m_MaxBlockCount > 1)) + { + return VK_ERROR_FEATURE_NOT_PRESENT; + } + + // Early reject: requested allocation size is larger that maximum block size for this block vector. + if (size + VMA_DEBUG_MARGIN > m_PreferredBlockSize) + { + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + + // 1. Search existing allocations. Try to allocate. + if (m_Algorithm == VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) + { + // Use only last block. + if (!m_Blocks.empty()) + { + VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks.back(); + VMA_ASSERT(pCurrBlock); + VkResult res = AllocateFromBlock( + pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation); + if (res == VK_SUCCESS) + { + VMA_DEBUG_LOG_FORMAT(" Returned from last block #%u", pCurrBlock->GetId()); + IncrementallySortBlocks(); + return VK_SUCCESS; + } + } + } + else + { + if (strategy != VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT) // MIN_MEMORY or default + { + const bool isHostVisible = + (m_hAllocator->m_MemProps.memoryTypes[m_MemoryTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0; + if(isHostVisible) + { + const bool isMappingAllowed = (createInfo.flags & + (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0; + /* + For non-mappable allocations, check blocks that are not mapped first. + For mappable allocations, check blocks that are already mapped first. + This way, having many blocks, we will separate mappable and non-mappable allocations, + hopefully limiting the number of blocks that are mapped, which will help tools like RenderDoc. + */ + for(size_t mappingI = 0; mappingI < 2; ++mappingI) + { + // Forward order in m_Blocks - prefer blocks with smallest amount of free space. + for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) + { + VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pCurrBlock); + const bool isBlockMapped = pCurrBlock->GetMappedData() != VMA_NULL; + if((mappingI == 0) == (isMappingAllowed == isBlockMapped)) + { + VkResult res = AllocateFromBlock( + pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation); + if (res == VK_SUCCESS) + { + VMA_DEBUG_LOG_FORMAT(" Returned from existing block #%u", pCurrBlock->GetId()); + IncrementallySortBlocks(); + return VK_SUCCESS; + } + } + } + } + } + else + { + // Forward order in m_Blocks - prefer blocks with smallest amount of free space. + for (size_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) + { + VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pCurrBlock); + VkResult res = AllocateFromBlock( + pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation); + if (res == VK_SUCCESS) + { + VMA_DEBUG_LOG_FORMAT(" Returned from existing block #%u", pCurrBlock->GetId()); + IncrementallySortBlocks(); + return VK_SUCCESS; + } + } + } + } + else // VMA_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT + { + // Backward order in m_Blocks - prefer blocks with largest amount of free space. + for (size_t blockIndex = m_Blocks.size(); blockIndex--; ) + { + VmaDeviceMemoryBlock* const pCurrBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pCurrBlock); + VkResult res = AllocateFromBlock(pCurrBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation); + if (res == VK_SUCCESS) + { + VMA_DEBUG_LOG_FORMAT(" Returned from existing block #%u", pCurrBlock->GetId()); + IncrementallySortBlocks(); + return VK_SUCCESS; + } + } + } + } + + // 2. Try to create new block. + if (canCreateNewBlock) + { + // Calculate optimal size for new block. + VkDeviceSize newBlockSize = m_PreferredBlockSize; + uint32_t newBlockSizeShift = 0; + const uint32_t NEW_BLOCK_SIZE_SHIFT_MAX = 3; + + if (!m_ExplicitBlockSize) + { + // Allocate 1/8, 1/4, 1/2 as first blocks. + const VkDeviceSize maxExistingBlockSize = CalcMaxBlockSize(); + for (uint32_t i = 0; i < NEW_BLOCK_SIZE_SHIFT_MAX; ++i) + { + const VkDeviceSize smallerNewBlockSize = newBlockSize / 2; + if (smallerNewBlockSize > maxExistingBlockSize && smallerNewBlockSize >= size * 2) + { + newBlockSize = smallerNewBlockSize; + ++newBlockSizeShift; + } + else + { + break; + } + } + } + + size_t newBlockIndex = 0; + VkResult res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ? + CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY; + // Allocation of this size failed? Try 1/2, 1/4, 1/8 of m_PreferredBlockSize. + if (!m_ExplicitBlockSize) + { + while (res < 0 && newBlockSizeShift < NEW_BLOCK_SIZE_SHIFT_MAX) + { + const VkDeviceSize smallerNewBlockSize = newBlockSize / 2; + if (smallerNewBlockSize >= size) + { + newBlockSize = smallerNewBlockSize; + ++newBlockSizeShift; + res = (newBlockSize <= freeMemory || !canFallbackToDedicated) ? + CreateBlock(newBlockSize, &newBlockIndex) : VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + else + { + break; + } + } + } + + if (res == VK_SUCCESS) + { + VmaDeviceMemoryBlock* const pBlock = m_Blocks[newBlockIndex]; + VMA_ASSERT(pBlock->m_pMetadata->GetSize() >= size); + + res = AllocateFromBlock( + pBlock, size, alignment, createInfo.flags, createInfo.pUserData, suballocType, strategy, pAllocation); + if (res == VK_SUCCESS) + { + VMA_DEBUG_LOG_FORMAT(" Created new block #%u Size=%llu", pBlock->GetId(), newBlockSize); + IncrementallySortBlocks(); + return VK_SUCCESS; + } + else + { + // Allocation from new block failed, possibly due to VMA_DEBUG_MARGIN or alignment. + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + } + } + + return VK_ERROR_OUT_OF_DEVICE_MEMORY; +} + +void VmaBlockVector::Free(const VmaAllocation hAllocation) +{ + VmaDeviceMemoryBlock* pBlockToDelete = VMA_NULL; + + bool budgetExceeded = false; + { + const uint32_t heapIndex = m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex); + VmaBudget heapBudget = {}; + m_hAllocator->GetHeapBudgets(&heapBudget, heapIndex, 1); + budgetExceeded = heapBudget.usage >= heapBudget.budget; + } + + // Scope for lock. + { + VmaMutexLockWrite lock(m_Mutex, m_hAllocator->m_UseMutex); + + VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock(); + + if (IsCorruptionDetectionEnabled()) + { + VkResult res = pBlock->ValidateMagicValueAfterAllocation(m_hAllocator, hAllocation->GetOffset(), hAllocation->GetSize()); + VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to validate magic value."); + } + + if (hAllocation->IsPersistentMap()) + { + pBlock->Unmap(m_hAllocator, 1); + } + + const bool hadEmptyBlockBeforeFree = HasEmptyBlock(); + pBlock->m_pMetadata->Free(hAllocation->GetAllocHandle()); + pBlock->PostFree(m_hAllocator); + VMA_HEAVY_ASSERT(pBlock->Validate()); + + VMA_DEBUG_LOG_FORMAT(" Freed from MemoryTypeIndex=%u", m_MemoryTypeIndex); + + const bool canDeleteBlock = m_Blocks.size() > m_MinBlockCount; + // pBlock became empty after this deallocation. + if (pBlock->m_pMetadata->IsEmpty()) + { + // Already had empty block. We don't want to have two, so delete this one. + if ((hadEmptyBlockBeforeFree || budgetExceeded) && canDeleteBlock) + { + pBlockToDelete = pBlock; + Remove(pBlock); + } + // else: We now have one empty block - leave it. A hysteresis to avoid allocating whole block back and forth. + } + // pBlock didn't become empty, but we have another empty block - find and free that one. + // (This is optional, heuristics.) + else if (hadEmptyBlockBeforeFree && canDeleteBlock) + { + VmaDeviceMemoryBlock* pLastBlock = m_Blocks.back(); + if (pLastBlock->m_pMetadata->IsEmpty()) + { + pBlockToDelete = pLastBlock; + m_Blocks.pop_back(); + } + } + + IncrementallySortBlocks(); + } + + // Destruction of a free block. Deferred until this point, outside of mutex + // lock, for performance reason. + if (pBlockToDelete != VMA_NULL) + { + VMA_DEBUG_LOG_FORMAT(" Deleted empty block #%u", pBlockToDelete->GetId()); + pBlockToDelete->Destroy(m_hAllocator); + vma_delete(m_hAllocator, pBlockToDelete); + } + + m_hAllocator->m_Budget.RemoveAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), hAllocation->GetSize()); + m_hAllocator->m_AllocationObjectAllocator.Free(hAllocation); +} + +VkDeviceSize VmaBlockVector::CalcMaxBlockSize() const +{ + VkDeviceSize result = 0; + for (size_t i = m_Blocks.size(); i--; ) + { + result = VMA_MAX(result, m_Blocks[i]->m_pMetadata->GetSize()); + if (result >= m_PreferredBlockSize) + { + break; + } + } + return result; +} + +void VmaBlockVector::Remove(VmaDeviceMemoryBlock* pBlock) +{ + for (uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) + { + if (m_Blocks[blockIndex] == pBlock) + { + VmaVectorRemove(m_Blocks, blockIndex); + return; + } + } + VMA_ASSERT(0); +} + +void VmaBlockVector::IncrementallySortBlocks() +{ + if (!m_IncrementalSort) + return; + if (m_Algorithm != VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) + { + // Bubble sort only until first swap. + for (size_t i = 1; i < m_Blocks.size(); ++i) + { + if (m_Blocks[i - 1]->m_pMetadata->GetSumFreeSize() > m_Blocks[i]->m_pMetadata->GetSumFreeSize()) + { + VMA_SWAP(m_Blocks[i - 1], m_Blocks[i]); + return; + } + } + } +} + +void VmaBlockVector::SortByFreeSize() +{ + VMA_SORT(m_Blocks.begin(), m_Blocks.end(), + [](VmaDeviceMemoryBlock* b1, VmaDeviceMemoryBlock* b2) -> bool + { + return b1->m_pMetadata->GetSumFreeSize() < b2->m_pMetadata->GetSumFreeSize(); + }); +} + +VkResult VmaBlockVector::AllocateFromBlock( + VmaDeviceMemoryBlock* pBlock, + VkDeviceSize size, + VkDeviceSize alignment, + VmaAllocationCreateFlags allocFlags, + void* pUserData, + VmaSuballocationType suballocType, + uint32_t strategy, + VmaAllocation* pAllocation) +{ + const bool isUpperAddress = (allocFlags & VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT) != 0; + + VmaAllocationRequest currRequest = {}; + if (pBlock->m_pMetadata->CreateAllocationRequest( + size, + alignment, + isUpperAddress, + suballocType, + strategy, + &currRequest)) + { + return CommitAllocationRequest(currRequest, pBlock, alignment, allocFlags, pUserData, suballocType, pAllocation); + } + return VK_ERROR_OUT_OF_DEVICE_MEMORY; +} + +VkResult VmaBlockVector::CommitAllocationRequest( + VmaAllocationRequest& allocRequest, + VmaDeviceMemoryBlock* pBlock, + VkDeviceSize alignment, + VmaAllocationCreateFlags allocFlags, + void* pUserData, + VmaSuballocationType suballocType, + VmaAllocation* pAllocation) +{ + const bool mapped = (allocFlags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0; + const bool isUserDataString = (allocFlags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0; + const bool isMappingAllowed = (allocFlags & + (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0; + + pBlock->PostAlloc(m_hAllocator); + // Allocate from pCurrBlock. + if (mapped) + { + VkResult res = pBlock->Map(m_hAllocator, 1, VMA_NULL); + if (res != VK_SUCCESS) + { + return res; + } + } + + *pAllocation = m_hAllocator->m_AllocationObjectAllocator.Allocate(isMappingAllowed); + pBlock->m_pMetadata->Alloc(allocRequest, suballocType, *pAllocation); + (*pAllocation)->InitBlockAllocation( + pBlock, + allocRequest.allocHandle, + alignment, + allocRequest.size, // Not size, as actual allocation size may be larger than requested! + m_MemoryTypeIndex, + suballocType, + mapped); + VMA_HEAVY_ASSERT(pBlock->Validate()); + if (isUserDataString) + (*pAllocation)->SetName(m_hAllocator, (const char*)pUserData); + else + (*pAllocation)->SetUserData(m_hAllocator, pUserData); + m_hAllocator->m_Budget.AddAllocation(m_hAllocator->MemoryTypeIndexToHeapIndex(m_MemoryTypeIndex), allocRequest.size); + if (VMA_DEBUG_INITIALIZE_ALLOCATIONS) + { + m_hAllocator->FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED); + } + if (IsCorruptionDetectionEnabled()) + { + VkResult res = pBlock->WriteMagicValueAfterAllocation(m_hAllocator, (*pAllocation)->GetOffset(), allocRequest.size); + VMA_ASSERT(res == VK_SUCCESS && "Couldn't map block memory to write magic value."); + } + return VK_SUCCESS; +} + +VkResult VmaBlockVector::CreateBlock(VkDeviceSize blockSize, size_t* pNewBlockIndex) +{ + VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + allocInfo.pNext = m_pMemoryAllocateNext; + allocInfo.memoryTypeIndex = m_MemoryTypeIndex; + allocInfo.allocationSize = blockSize; + +#if VMA_BUFFER_DEVICE_ADDRESS + // Every standalone block can potentially contain a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT - always enable the feature. + VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR }; + if (m_hAllocator->m_UseKhrBufferDeviceAddress) + { + allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR; + VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo); + } +#endif // VMA_BUFFER_DEVICE_ADDRESS + +#if VMA_MEMORY_PRIORITY + VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT }; + if (m_hAllocator->m_UseExtMemoryPriority) + { + VMA_ASSERT(m_Priority >= 0.f && m_Priority <= 1.f); + priorityInfo.priority = m_Priority; + VmaPnextChainPushFront(&allocInfo, &priorityInfo); + } +#endif // VMA_MEMORY_PRIORITY + +#if VMA_EXTERNAL_MEMORY + // Attach VkExportMemoryAllocateInfoKHR if necessary. + VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR }; + exportMemoryAllocInfo.handleTypes = m_hAllocator->GetExternalMemoryHandleTypeFlags(m_MemoryTypeIndex); + if (exportMemoryAllocInfo.handleTypes != 0) + { + VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo); + } +#endif // VMA_EXTERNAL_MEMORY + + VkDeviceMemory mem = VK_NULL_HANDLE; + VkResult res = m_hAllocator->AllocateVulkanMemory(&allocInfo, &mem); + if (res < 0) + { + return res; + } + + // New VkDeviceMemory successfully created. + + // Create new Allocation for it. + VmaDeviceMemoryBlock* const pBlock = vma_new(m_hAllocator, VmaDeviceMemoryBlock)(m_hAllocator); + pBlock->Init( + m_hAllocator, + m_hParentPool, + m_MemoryTypeIndex, + mem, + allocInfo.allocationSize, + m_NextBlockId++, + m_Algorithm, + m_BufferImageGranularity); + + m_Blocks.push_back(pBlock); + if (pNewBlockIndex != VMA_NULL) + { + *pNewBlockIndex = m_Blocks.size() - 1; + } + + return VK_SUCCESS; +} + +bool VmaBlockVector::HasEmptyBlock() +{ + for (size_t index = 0, count = m_Blocks.size(); index < count; ++index) + { + VmaDeviceMemoryBlock* const pBlock = m_Blocks[index]; + if (pBlock->m_pMetadata->IsEmpty()) + { + return true; + } + } + return false; +} + +#if VMA_STATS_STRING_ENABLED +void VmaBlockVector::PrintDetailedMap(class VmaJsonWriter& json) +{ + VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); + + + json.BeginObject(); + for (size_t i = 0; i < m_Blocks.size(); ++i) + { + json.BeginString(); + json.ContinueString(m_Blocks[i]->GetId()); + json.EndString(); + + json.BeginObject(); + json.WriteString("MapRefCount"); + json.WriteNumber(m_Blocks[i]->GetMapRefCount()); + + m_Blocks[i]->m_pMetadata->PrintDetailedMap(json); + json.EndObject(); + } + json.EndObject(); +} +#endif // VMA_STATS_STRING_ENABLED + +VkResult VmaBlockVector::CheckCorruption() +{ + if (!IsCorruptionDetectionEnabled()) + { + return VK_ERROR_FEATURE_NOT_PRESENT; + } + + VmaMutexLockRead lock(m_Mutex, m_hAllocator->m_UseMutex); + for (uint32_t blockIndex = 0; blockIndex < m_Blocks.size(); ++blockIndex) + { + VmaDeviceMemoryBlock* const pBlock = m_Blocks[blockIndex]; + VMA_ASSERT(pBlock); + VkResult res = pBlock->CheckCorruption(m_hAllocator); + if (res != VK_SUCCESS) + { + return res; + } + } + return VK_SUCCESS; +} + +#endif // _VMA_BLOCK_VECTOR_FUNCTIONS + +#ifndef _VMA_DEFRAGMENTATION_CONTEXT_FUNCTIONS +VmaDefragmentationContext_T::VmaDefragmentationContext_T( + VmaAllocator hAllocator, + const VmaDefragmentationInfo& info) + : m_MaxPassBytes(info.maxBytesPerPass == 0 ? VK_WHOLE_SIZE : info.maxBytesPerPass), + m_MaxPassAllocations(info.maxAllocationsPerPass == 0 ? UINT32_MAX : info.maxAllocationsPerPass), + m_MoveAllocator(hAllocator->GetAllocationCallbacks()), + m_Moves(m_MoveAllocator) +{ + m_Algorithm = info.flags & VMA_DEFRAGMENTATION_FLAG_ALGORITHM_MASK; + + if (info.pool != VMA_NULL) + { + m_BlockVectorCount = 1; + m_PoolBlockVector = &info.pool->m_BlockVector; + m_pBlockVectors = &m_PoolBlockVector; + m_PoolBlockVector->SetIncrementalSort(false); + m_PoolBlockVector->SortByFreeSize(); + } + else + { + m_BlockVectorCount = hAllocator->GetMemoryTypeCount(); + m_PoolBlockVector = VMA_NULL; + m_pBlockVectors = hAllocator->m_pBlockVectors; + for (uint32_t i = 0; i < m_BlockVectorCount; ++i) + { + VmaBlockVector* vector = m_pBlockVectors[i]; + if (vector != VMA_NULL) + { + vector->SetIncrementalSort(false); + vector->SortByFreeSize(); + } + } + } + + switch (m_Algorithm) + { + case 0: // Default algorithm + m_Algorithm = VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT; + m_AlgorithmState = vma_new_array(hAllocator, StateBalanced, m_BlockVectorCount); + break; + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT: + m_AlgorithmState = vma_new_array(hAllocator, StateBalanced, m_BlockVectorCount); + break; + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT: + if (hAllocator->GetBufferImageGranularity() > 1) + { + m_AlgorithmState = vma_new_array(hAllocator, StateExtensive, m_BlockVectorCount); + } + break; + } +} + +VmaDefragmentationContext_T::~VmaDefragmentationContext_T() +{ + if (m_PoolBlockVector != VMA_NULL) + { + m_PoolBlockVector->SetIncrementalSort(true); + } + else + { + for (uint32_t i = 0; i < m_BlockVectorCount; ++i) + { + VmaBlockVector* vector = m_pBlockVectors[i]; + if (vector != VMA_NULL) + vector->SetIncrementalSort(true); + } + } + + if (m_AlgorithmState) + { + switch (m_Algorithm) + { + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT: + vma_delete_array(m_MoveAllocator.m_pCallbacks, reinterpret_cast(m_AlgorithmState), m_BlockVectorCount); + break; + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT: + vma_delete_array(m_MoveAllocator.m_pCallbacks, reinterpret_cast(m_AlgorithmState), m_BlockVectorCount); + break; + default: + VMA_ASSERT(0); + } + } +} + +VkResult VmaDefragmentationContext_T::DefragmentPassBegin(VmaDefragmentationPassMoveInfo& moveInfo) +{ + if (m_PoolBlockVector != VMA_NULL) + { + VmaMutexLockWrite lock(m_PoolBlockVector->GetMutex(), m_PoolBlockVector->GetAllocator()->m_UseMutex); + + if (m_PoolBlockVector->GetBlockCount() > 1) + ComputeDefragmentation(*m_PoolBlockVector, 0); + else if (m_PoolBlockVector->GetBlockCount() == 1) + ReallocWithinBlock(*m_PoolBlockVector, m_PoolBlockVector->GetBlock(0)); + } + else + { + for (uint32_t i = 0; i < m_BlockVectorCount; ++i) + { + if (m_pBlockVectors[i] != VMA_NULL) + { + VmaMutexLockWrite lock(m_pBlockVectors[i]->GetMutex(), m_pBlockVectors[i]->GetAllocator()->m_UseMutex); + + if (m_pBlockVectors[i]->GetBlockCount() > 1) + { + if (ComputeDefragmentation(*m_pBlockVectors[i], i)) + break; + } + else if (m_pBlockVectors[i]->GetBlockCount() == 1) + { + if (ReallocWithinBlock(*m_pBlockVectors[i], m_pBlockVectors[i]->GetBlock(0))) + break; + } + } + } + } + + moveInfo.moveCount = static_cast(m_Moves.size()); + if (moveInfo.moveCount > 0) + { + moveInfo.pMoves = m_Moves.data(); + return VK_INCOMPLETE; + } + + moveInfo.pMoves = VMA_NULL; + return VK_SUCCESS; +} + +VkResult VmaDefragmentationContext_T::DefragmentPassEnd(VmaDefragmentationPassMoveInfo& moveInfo) +{ + VMA_ASSERT(moveInfo.moveCount > 0 ? moveInfo.pMoves != VMA_NULL : true); + + VkResult result = VK_SUCCESS; + VmaStlAllocator blockAllocator(m_MoveAllocator.m_pCallbacks); + VmaVector> immovableBlocks(blockAllocator); + VmaVector> mappedBlocks(blockAllocator); + + VmaAllocator allocator = VMA_NULL; + for (uint32_t i = 0; i < moveInfo.moveCount; ++i) + { + VmaDefragmentationMove& move = moveInfo.pMoves[i]; + size_t prevCount = 0, currentCount = 0; + VkDeviceSize freedBlockSize = 0; + + uint32_t vectorIndex; + VmaBlockVector* vector; + if (m_PoolBlockVector != VMA_NULL) + { + vectorIndex = 0; + vector = m_PoolBlockVector; + } + else + { + vectorIndex = move.srcAllocation->GetMemoryTypeIndex(); + vector = m_pBlockVectors[vectorIndex]; + VMA_ASSERT(vector != VMA_NULL); + } + + switch (move.operation) + { + case VMA_DEFRAGMENTATION_MOVE_OPERATION_COPY: + { + uint8_t mapCount = move.srcAllocation->SwapBlockAllocation(vector->m_hAllocator, move.dstTmpAllocation); + if (mapCount > 0) + { + allocator = vector->m_hAllocator; + VmaDeviceMemoryBlock* newMapBlock = move.srcAllocation->GetBlock(); + bool notPresent = true; + for (FragmentedBlock& block : mappedBlocks) + { + if (block.block == newMapBlock) + { + notPresent = false; + block.data += mapCount; + break; + } + } + if (notPresent) + mappedBlocks.push_back({ mapCount, newMapBlock }); + } + + // Scope for locks, Free have it's own lock + { + VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex); + prevCount = vector->GetBlockCount(); + freedBlockSize = move.dstTmpAllocation->GetBlock()->m_pMetadata->GetSize(); + } + vector->Free(move.dstTmpAllocation); + { + VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex); + currentCount = vector->GetBlockCount(); + } + + result = VK_INCOMPLETE; + break; + } + case VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE: + { + m_PassStats.bytesMoved -= move.srcAllocation->GetSize(); + --m_PassStats.allocationsMoved; + vector->Free(move.dstTmpAllocation); + + VmaDeviceMemoryBlock* newBlock = move.srcAllocation->GetBlock(); + bool notPresent = true; + for (const FragmentedBlock& block : immovableBlocks) + { + if (block.block == newBlock) + { + notPresent = false; + break; + } + } + if (notPresent) + immovableBlocks.push_back({ vectorIndex, newBlock }); + break; + } + case VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY: + { + m_PassStats.bytesMoved -= move.srcAllocation->GetSize(); + --m_PassStats.allocationsMoved; + // Scope for locks, Free have it's own lock + { + VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex); + prevCount = vector->GetBlockCount(); + freedBlockSize = move.srcAllocation->GetBlock()->m_pMetadata->GetSize(); + } + vector->Free(move.srcAllocation); + { + VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex); + currentCount = vector->GetBlockCount(); + } + freedBlockSize *= prevCount - currentCount; + + VkDeviceSize dstBlockSize; + { + VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex); + dstBlockSize = move.dstTmpAllocation->GetBlock()->m_pMetadata->GetSize(); + } + vector->Free(move.dstTmpAllocation); + { + VmaMutexLockRead lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex); + freedBlockSize += dstBlockSize * (currentCount - vector->GetBlockCount()); + currentCount = vector->GetBlockCount(); + } + + result = VK_INCOMPLETE; + break; + } + default: + VMA_ASSERT(0); + } + + if (prevCount > currentCount) + { + size_t freedBlocks = prevCount - currentCount; + m_PassStats.deviceMemoryBlocksFreed += static_cast(freedBlocks); + m_PassStats.bytesFreed += freedBlockSize; + } + + switch (m_Algorithm) + { + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT: + { + if (m_AlgorithmState != VMA_NULL) + { + // Avoid unnecessary tries to allocate when new free block is available + StateExtensive& state = reinterpret_cast(m_AlgorithmState)[vectorIndex]; + if (state.firstFreeBlock != SIZE_MAX) + { + const size_t diff = prevCount - currentCount; + if (state.firstFreeBlock >= diff) + { + state.firstFreeBlock -= diff; + if (state.firstFreeBlock != 0) + state.firstFreeBlock -= vector->GetBlock(state.firstFreeBlock - 1)->m_pMetadata->IsEmpty(); + } + else + state.firstFreeBlock = 0; + } + } + } + } + } + moveInfo.moveCount = 0; + moveInfo.pMoves = VMA_NULL; + m_Moves.clear(); + + // Update stats + m_GlobalStats.allocationsMoved += m_PassStats.allocationsMoved; + m_GlobalStats.bytesFreed += m_PassStats.bytesFreed; + m_GlobalStats.bytesMoved += m_PassStats.bytesMoved; + m_GlobalStats.deviceMemoryBlocksFreed += m_PassStats.deviceMemoryBlocksFreed; + m_PassStats = { 0 }; + + // Move blocks with immovable allocations according to algorithm + if (immovableBlocks.size() > 0) + { + do + { + if(m_Algorithm == VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT) + { + if (m_AlgorithmState != VMA_NULL) + { + bool swapped = false; + // Move to the start of free blocks range + for (const FragmentedBlock& block : immovableBlocks) + { + StateExtensive& state = reinterpret_cast(m_AlgorithmState)[block.data]; + if (state.operation != StateExtensive::Operation::Cleanup) + { + VmaBlockVector* vector = m_pBlockVectors[block.data]; + VmaMutexLockWrite lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex); + + for (size_t i = 0, count = vector->GetBlockCount() - m_ImmovableBlockCount; i < count; ++i) + { + if (vector->GetBlock(i) == block.block) + { + VMA_SWAP(vector->m_Blocks[i], vector->m_Blocks[vector->GetBlockCount() - ++m_ImmovableBlockCount]); + if (state.firstFreeBlock != SIZE_MAX) + { + if (i + 1 < state.firstFreeBlock) + { + if (state.firstFreeBlock > 1) + VMA_SWAP(vector->m_Blocks[i], vector->m_Blocks[--state.firstFreeBlock]); + else + --state.firstFreeBlock; + } + } + swapped = true; + break; + } + } + } + } + if (swapped) + result = VK_INCOMPLETE; + break; + } + } + + // Move to the beginning + for (const FragmentedBlock& block : immovableBlocks) + { + VmaBlockVector* vector = m_pBlockVectors[block.data]; + VmaMutexLockWrite lock(vector->GetMutex(), vector->GetAllocator()->m_UseMutex); + + for (size_t i = m_ImmovableBlockCount; i < vector->GetBlockCount(); ++i) + { + if (vector->GetBlock(i) == block.block) + { + VMA_SWAP(vector->m_Blocks[i], vector->m_Blocks[m_ImmovableBlockCount++]); + break; + } + } + } + } while (false); + } + + // Bulk-map destination blocks + for (const FragmentedBlock& block : mappedBlocks) + { + VkResult res = block.block->Map(allocator, block.data, VMA_NULL); + VMA_ASSERT(res == VK_SUCCESS); + } + return result; +} + +bool VmaDefragmentationContext_T::ComputeDefragmentation(VmaBlockVector& vector, size_t index) +{ + switch (m_Algorithm) + { + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT: + return ComputeDefragmentation_Fast(vector); + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_BALANCED_BIT: + return ComputeDefragmentation_Balanced(vector, index, true); + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FULL_BIT: + return ComputeDefragmentation_Full(vector); + case VMA_DEFRAGMENTATION_FLAG_ALGORITHM_EXTENSIVE_BIT: + return ComputeDefragmentation_Extensive(vector, index); + default: + VMA_ASSERT(0); + return ComputeDefragmentation_Balanced(vector, index, true); + } +} + +VmaDefragmentationContext_T::MoveAllocationData VmaDefragmentationContext_T::GetMoveData( + VmaAllocHandle handle, VmaBlockMetadata* metadata) +{ + MoveAllocationData moveData; + moveData.move.srcAllocation = (VmaAllocation)metadata->GetAllocationUserData(handle); + moveData.size = moveData.move.srcAllocation->GetSize(); + moveData.alignment = moveData.move.srcAllocation->GetAlignment(); + moveData.type = moveData.move.srcAllocation->GetSuballocationType(); + moveData.flags = 0; + + if (moveData.move.srcAllocation->IsPersistentMap()) + moveData.flags |= VMA_ALLOCATION_CREATE_MAPPED_BIT; + if (moveData.move.srcAllocation->IsMappingAllowed()) + moveData.flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT; + + return moveData; +} + +VmaDefragmentationContext_T::CounterStatus VmaDefragmentationContext_T::CheckCounters(VkDeviceSize bytes) +{ + // Ignore allocation if will exceed max size for copy + if (m_PassStats.bytesMoved + bytes > m_MaxPassBytes) + { + if (++m_IgnoredAllocs < MAX_ALLOCS_TO_IGNORE) + return CounterStatus::Ignore; + else + return CounterStatus::End; + } + return CounterStatus::Pass; +} + +bool VmaDefragmentationContext_T::IncrementCounters(VkDeviceSize bytes) +{ + m_PassStats.bytesMoved += bytes; + // Early return when max found + if (++m_PassStats.allocationsMoved >= m_MaxPassAllocations || m_PassStats.bytesMoved >= m_MaxPassBytes) + { + VMA_ASSERT(m_PassStats.allocationsMoved == m_MaxPassAllocations || + m_PassStats.bytesMoved == m_MaxPassBytes && "Exceeded maximal pass threshold!"); + return true; + } + return false; +} + +bool VmaDefragmentationContext_T::ReallocWithinBlock(VmaBlockVector& vector, VmaDeviceMemoryBlock* block) +{ + VmaBlockMetadata* metadata = block->m_pMetadata; + + for (VmaAllocHandle handle = metadata->GetAllocationListBegin(); + handle != VK_NULL_HANDLE; + handle = metadata->GetNextAllocation(handle)) + { + MoveAllocationData moveData = GetMoveData(handle, metadata); + // Ignore newly created allocations by defragmentation algorithm + if (moveData.move.srcAllocation->GetUserData() == this) + continue; + switch (CheckCounters(moveData.move.srcAllocation->GetSize())) + { + case CounterStatus::Ignore: + continue; + case CounterStatus::End: + return true; + case CounterStatus::Pass: + break; + default: + VMA_ASSERT(0); + } + + VkDeviceSize offset = moveData.move.srcAllocation->GetOffset(); + if (offset != 0 && metadata->GetSumFreeSize() >= moveData.size) + { + VmaAllocationRequest request = {}; + if (metadata->CreateAllocationRequest( + moveData.size, + moveData.alignment, + false, + moveData.type, + VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT, + &request)) + { + if (metadata->GetAllocationOffset(request.allocHandle) < offset) + { + if (vector.CommitAllocationRequest( + request, + block, + moveData.alignment, + moveData.flags, + this, + moveData.type, + &moveData.move.dstTmpAllocation) == VK_SUCCESS) + { + m_Moves.push_back(moveData.move); + if (IncrementCounters(moveData.size)) + return true; + } + } + } + } + } + return false; +} + +bool VmaDefragmentationContext_T::AllocInOtherBlock(size_t start, size_t end, MoveAllocationData& data, VmaBlockVector& vector) +{ + for (; start < end; ++start) + { + VmaDeviceMemoryBlock* dstBlock = vector.GetBlock(start); + if (dstBlock->m_pMetadata->GetSumFreeSize() >= data.size) + { + if (vector.AllocateFromBlock(dstBlock, + data.size, + data.alignment, + data.flags, + this, + data.type, + 0, + &data.move.dstTmpAllocation) == VK_SUCCESS) + { + m_Moves.push_back(data.move); + if (IncrementCounters(data.size)) + return true; + break; + } + } + } + return false; +} + +bool VmaDefragmentationContext_T::ComputeDefragmentation_Fast(VmaBlockVector& vector) +{ + // Move only between blocks + + // Go through allocations in last blocks and try to fit them inside first ones + for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i) + { + VmaBlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata; + + for (VmaAllocHandle handle = metadata->GetAllocationListBegin(); + handle != VK_NULL_HANDLE; + handle = metadata->GetNextAllocation(handle)) + { + MoveAllocationData moveData = GetMoveData(handle, metadata); + // Ignore newly created allocations by defragmentation algorithm + if (moveData.move.srcAllocation->GetUserData() == this) + continue; + switch (CheckCounters(moveData.move.srcAllocation->GetSize())) + { + case CounterStatus::Ignore: + continue; + case CounterStatus::End: + return true; + case CounterStatus::Pass: + break; + default: + VMA_ASSERT(0); + } + + // Check all previous blocks for free space + if (AllocInOtherBlock(0, i, moveData, vector)) + return true; + } + } + return false; +} + +bool VmaDefragmentationContext_T::ComputeDefragmentation_Balanced(VmaBlockVector& vector, size_t index, bool update) +{ + // Go over every allocation and try to fit it in previous blocks at lowest offsets, + // if not possible: realloc within single block to minimize offset (exclude offset == 0), + // but only if there are noticeable gaps between them (some heuristic, ex. average size of allocation in block) + VMA_ASSERT(m_AlgorithmState != VMA_NULL); + + StateBalanced& vectorState = reinterpret_cast(m_AlgorithmState)[index]; + if (update && vectorState.avgAllocSize == UINT64_MAX) + UpdateVectorStatistics(vector, vectorState); + + const size_t startMoveCount = m_Moves.size(); + VkDeviceSize minimalFreeRegion = vectorState.avgFreeSize / 2; + for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i) + { + VmaDeviceMemoryBlock* block = vector.GetBlock(i); + VmaBlockMetadata* metadata = block->m_pMetadata; + VkDeviceSize prevFreeRegionSize = 0; + + for (VmaAllocHandle handle = metadata->GetAllocationListBegin(); + handle != VK_NULL_HANDLE; + handle = metadata->GetNextAllocation(handle)) + { + MoveAllocationData moveData = GetMoveData(handle, metadata); + // Ignore newly created allocations by defragmentation algorithm + if (moveData.move.srcAllocation->GetUserData() == this) + continue; + switch (CheckCounters(moveData.move.srcAllocation->GetSize())) + { + case CounterStatus::Ignore: + continue; + case CounterStatus::End: + return true; + case CounterStatus::Pass: + break; + default: + VMA_ASSERT(0); + } + + // Check all previous blocks for free space + const size_t prevMoveCount = m_Moves.size(); + if (AllocInOtherBlock(0, i, moveData, vector)) + return true; + + VkDeviceSize nextFreeRegionSize = metadata->GetNextFreeRegionSize(handle); + // If no room found then realloc within block for lower offset + VkDeviceSize offset = moveData.move.srcAllocation->GetOffset(); + if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size) + { + // Check if realloc will make sense + if (prevFreeRegionSize >= minimalFreeRegion || + nextFreeRegionSize >= minimalFreeRegion || + moveData.size <= vectorState.avgFreeSize || + moveData.size <= vectorState.avgAllocSize) + { + VmaAllocationRequest request = {}; + if (metadata->CreateAllocationRequest( + moveData.size, + moveData.alignment, + false, + moveData.type, + VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT, + &request)) + { + if (metadata->GetAllocationOffset(request.allocHandle) < offset) + { + if (vector.CommitAllocationRequest( + request, + block, + moveData.alignment, + moveData.flags, + this, + moveData.type, + &moveData.move.dstTmpAllocation) == VK_SUCCESS) + { + m_Moves.push_back(moveData.move); + if (IncrementCounters(moveData.size)) + return true; + } + } + } + } + } + prevFreeRegionSize = nextFreeRegionSize; + } + } + + // No moves performed, update statistics to current vector state + if (startMoveCount == m_Moves.size() && !update) + { + vectorState.avgAllocSize = UINT64_MAX; + return ComputeDefragmentation_Balanced(vector, index, false); + } + return false; +} + +bool VmaDefragmentationContext_T::ComputeDefragmentation_Full(VmaBlockVector& vector) +{ + // Go over every allocation and try to fit it in previous blocks at lowest offsets, + // if not possible: realloc within single block to minimize offset (exclude offset == 0) + + for (size_t i = vector.GetBlockCount() - 1; i > m_ImmovableBlockCount; --i) + { + VmaDeviceMemoryBlock* block = vector.GetBlock(i); + VmaBlockMetadata* metadata = block->m_pMetadata; + + for (VmaAllocHandle handle = metadata->GetAllocationListBegin(); + handle != VK_NULL_HANDLE; + handle = metadata->GetNextAllocation(handle)) + { + MoveAllocationData moveData = GetMoveData(handle, metadata); + // Ignore newly created allocations by defragmentation algorithm + if (moveData.move.srcAllocation->GetUserData() == this) + continue; + switch (CheckCounters(moveData.move.srcAllocation->GetSize())) + { + case CounterStatus::Ignore: + continue; + case CounterStatus::End: + return true; + case CounterStatus::Pass: + break; + default: + VMA_ASSERT(0); + } + + // Check all previous blocks for free space + const size_t prevMoveCount = m_Moves.size(); + if (AllocInOtherBlock(0, i, moveData, vector)) + return true; + + // If no room found then realloc within block for lower offset + VkDeviceSize offset = moveData.move.srcAllocation->GetOffset(); + if (prevMoveCount == m_Moves.size() && offset != 0 && metadata->GetSumFreeSize() >= moveData.size) + { + VmaAllocationRequest request = {}; + if (metadata->CreateAllocationRequest( + moveData.size, + moveData.alignment, + false, + moveData.type, + VMA_ALLOCATION_CREATE_STRATEGY_MIN_OFFSET_BIT, + &request)) + { + if (metadata->GetAllocationOffset(request.allocHandle) < offset) + { + if (vector.CommitAllocationRequest( + request, + block, + moveData.alignment, + moveData.flags, + this, + moveData.type, + &moveData.move.dstTmpAllocation) == VK_SUCCESS) + { + m_Moves.push_back(moveData.move); + if (IncrementCounters(moveData.size)) + return true; + } + } + } + } + } + } + return false; +} + +bool VmaDefragmentationContext_T::ComputeDefragmentation_Extensive(VmaBlockVector& vector, size_t index) +{ + // First free single block, then populate it to the brim, then free another block, and so on + + // Fallback to previous algorithm since without granularity conflicts it can achieve max packing + if (vector.m_BufferImageGranularity == 1) + return ComputeDefragmentation_Full(vector); + + VMA_ASSERT(m_AlgorithmState != VMA_NULL); + + StateExtensive& vectorState = reinterpret_cast(m_AlgorithmState)[index]; + + bool texturePresent = false, bufferPresent = false, otherPresent = false; + switch (vectorState.operation) + { + case StateExtensive::Operation::Done: // Vector defragmented + return false; + case StateExtensive::Operation::FindFreeBlockBuffer: + case StateExtensive::Operation::FindFreeBlockTexture: + case StateExtensive::Operation::FindFreeBlockAll: + { + // No more blocks to free, just perform fast realloc and move to cleanup + if (vectorState.firstFreeBlock == 0) + { + vectorState.operation = StateExtensive::Operation::Cleanup; + return ComputeDefragmentation_Fast(vector); + } + + // No free blocks, have to clear last one + size_t last = (vectorState.firstFreeBlock == SIZE_MAX ? vector.GetBlockCount() : vectorState.firstFreeBlock) - 1; + VmaBlockMetadata* freeMetadata = vector.GetBlock(last)->m_pMetadata; + + const size_t prevMoveCount = m_Moves.size(); + for (VmaAllocHandle handle = freeMetadata->GetAllocationListBegin(); + handle != VK_NULL_HANDLE; + handle = freeMetadata->GetNextAllocation(handle)) + { + MoveAllocationData moveData = GetMoveData(handle, freeMetadata); + switch (CheckCounters(moveData.move.srcAllocation->GetSize())) + { + case CounterStatus::Ignore: + continue; + case CounterStatus::End: + return true; + case CounterStatus::Pass: + break; + default: + VMA_ASSERT(0); + } + + // Check all previous blocks for free space + if (AllocInOtherBlock(0, last, moveData, vector)) + { + // Full clear performed already + if (prevMoveCount != m_Moves.size() && freeMetadata->GetNextAllocation(handle) == VK_NULL_HANDLE) + reinterpret_cast(m_AlgorithmState)[index] = last; + return true; + } + } + + if (prevMoveCount == m_Moves.size()) + { + // Cannot perform full clear, have to move data in other blocks around + if (last != 0) + { + for (size_t i = last - 1; i; --i) + { + if (ReallocWithinBlock(vector, vector.GetBlock(i))) + return true; + } + } + + if (prevMoveCount == m_Moves.size()) + { + // No possible reallocs within blocks, try to move them around fast + return ComputeDefragmentation_Fast(vector); + } + } + else + { + switch (vectorState.operation) + { + case StateExtensive::Operation::FindFreeBlockBuffer: + vectorState.operation = StateExtensive::Operation::MoveBuffers; + break; + case StateExtensive::Operation::FindFreeBlockTexture: + vectorState.operation = StateExtensive::Operation::MoveTextures; + break; + case StateExtensive::Operation::FindFreeBlockAll: + vectorState.operation = StateExtensive::Operation::MoveAll; + break; + default: + VMA_ASSERT(0); + vectorState.operation = StateExtensive::Operation::MoveTextures; + } + vectorState.firstFreeBlock = last; + // Nothing done, block found without reallocations, can perform another reallocs in same pass + return ComputeDefragmentation_Extensive(vector, index); + } + break; + } + case StateExtensive::Operation::MoveTextures: + { + if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL, vector, + vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent)) + { + if (texturePresent) + { + vectorState.operation = StateExtensive::Operation::FindFreeBlockTexture; + return ComputeDefragmentation_Extensive(vector, index); + } + + if (!bufferPresent && !otherPresent) + { + vectorState.operation = StateExtensive::Operation::Cleanup; + break; + } + + // No more textures to move, check buffers + vectorState.operation = StateExtensive::Operation::MoveBuffers; + bufferPresent = false; + otherPresent = false; + } + else + break; + } + case StateExtensive::Operation::MoveBuffers: + { + if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_BUFFER, vector, + vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent)) + { + if (bufferPresent) + { + vectorState.operation = StateExtensive::Operation::FindFreeBlockBuffer; + return ComputeDefragmentation_Extensive(vector, index); + } + + if (!otherPresent) + { + vectorState.operation = StateExtensive::Operation::Cleanup; + break; + } + + // No more buffers to move, check all others + vectorState.operation = StateExtensive::Operation::MoveAll; + otherPresent = false; + } + else + break; + } + case StateExtensive::Operation::MoveAll: + { + if (MoveDataToFreeBlocks(VMA_SUBALLOCATION_TYPE_FREE, vector, + vectorState.firstFreeBlock, texturePresent, bufferPresent, otherPresent)) + { + if (otherPresent) + { + vectorState.operation = StateExtensive::Operation::FindFreeBlockBuffer; + return ComputeDefragmentation_Extensive(vector, index); + } + // Everything moved + vectorState.operation = StateExtensive::Operation::Cleanup; + } + break; + } + case StateExtensive::Operation::Cleanup: + // Cleanup is handled below so that other operations may reuse the cleanup code. This case is here to prevent the unhandled enum value warning (C4062). + break; + } + + if (vectorState.operation == StateExtensive::Operation::Cleanup) + { + // All other work done, pack data in blocks even tighter if possible + const size_t prevMoveCount = m_Moves.size(); + for (size_t i = 0; i < vector.GetBlockCount(); ++i) + { + if (ReallocWithinBlock(vector, vector.GetBlock(i))) + return true; + } + + if (prevMoveCount == m_Moves.size()) + vectorState.operation = StateExtensive::Operation::Done; + } + return false; +} + +void VmaDefragmentationContext_T::UpdateVectorStatistics(VmaBlockVector& vector, StateBalanced& state) +{ + size_t allocCount = 0; + size_t freeCount = 0; + state.avgFreeSize = 0; + state.avgAllocSize = 0; + + for (size_t i = 0; i < vector.GetBlockCount(); ++i) + { + VmaBlockMetadata* metadata = vector.GetBlock(i)->m_pMetadata; + + allocCount += metadata->GetAllocationCount(); + freeCount += metadata->GetFreeRegionsCount(); + state.avgFreeSize += metadata->GetSumFreeSize(); + state.avgAllocSize += metadata->GetSize(); + } + + state.avgAllocSize = (state.avgAllocSize - state.avgFreeSize) / allocCount; + state.avgFreeSize /= freeCount; +} + +bool VmaDefragmentationContext_T::MoveDataToFreeBlocks(VmaSuballocationType currentType, + VmaBlockVector& vector, size_t firstFreeBlock, + bool& texturePresent, bool& bufferPresent, bool& otherPresent) +{ + const size_t prevMoveCount = m_Moves.size(); + for (size_t i = firstFreeBlock ; i;) + { + VmaDeviceMemoryBlock* block = vector.GetBlock(--i); + VmaBlockMetadata* metadata = block->m_pMetadata; + + for (VmaAllocHandle handle = metadata->GetAllocationListBegin(); + handle != VK_NULL_HANDLE; + handle = metadata->GetNextAllocation(handle)) + { + MoveAllocationData moveData = GetMoveData(handle, metadata); + // Ignore newly created allocations by defragmentation algorithm + if (moveData.move.srcAllocation->GetUserData() == this) + continue; + switch (CheckCounters(moveData.move.srcAllocation->GetSize())) + { + case CounterStatus::Ignore: + continue; + case CounterStatus::End: + return true; + case CounterStatus::Pass: + break; + default: + VMA_ASSERT(0); + } + + // Move only single type of resources at once + if (!VmaIsBufferImageGranularityConflict(moveData.type, currentType)) + { + // Try to fit allocation into free blocks + if (AllocInOtherBlock(firstFreeBlock, vector.GetBlockCount(), moveData, vector)) + return false; + } + + if (!VmaIsBufferImageGranularityConflict(moveData.type, VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL)) + texturePresent = true; + else if (!VmaIsBufferImageGranularityConflict(moveData.type, VMA_SUBALLOCATION_TYPE_BUFFER)) + bufferPresent = true; + else + otherPresent = true; + } + } + return prevMoveCount == m_Moves.size(); +} +#endif // _VMA_DEFRAGMENTATION_CONTEXT_FUNCTIONS + +#ifndef _VMA_POOL_T_FUNCTIONS +VmaPool_T::VmaPool_T( + VmaAllocator hAllocator, + const VmaPoolCreateInfo& createInfo, + VkDeviceSize preferredBlockSize) + : m_BlockVector( + hAllocator, + this, // hParentPool + createInfo.memoryTypeIndex, + createInfo.blockSize != 0 ? createInfo.blockSize : preferredBlockSize, + createInfo.minBlockCount, + createInfo.maxBlockCount, + (createInfo.flags& VMA_POOL_CREATE_IGNORE_BUFFER_IMAGE_GRANULARITY_BIT) != 0 ? 1 : hAllocator->GetBufferImageGranularity(), + createInfo.blockSize != 0, // explicitBlockSize + createInfo.flags & VMA_POOL_CREATE_ALGORITHM_MASK, // algorithm + createInfo.priority, + VMA_MAX(hAllocator->GetMemoryTypeMinAlignment(createInfo.memoryTypeIndex), createInfo.minAllocationAlignment), + createInfo.pMemoryAllocateNext), + m_Id(0), + m_Name(VMA_NULL) {} + +VmaPool_T::~VmaPool_T() +{ + VMA_ASSERT(m_PrevPool == VMA_NULL && m_NextPool == VMA_NULL); +} + +void VmaPool_T::SetName(const char* pName) +{ + const VkAllocationCallbacks* allocs = m_BlockVector.GetAllocator()->GetAllocationCallbacks(); + VmaFreeString(allocs, m_Name); + + if (pName != VMA_NULL) + { + m_Name = VmaCreateStringCopy(allocs, pName); + } + else + { + m_Name = VMA_NULL; + } +} +#endif // _VMA_POOL_T_FUNCTIONS + +#ifndef _VMA_ALLOCATOR_T_FUNCTIONS +VmaAllocator_T::VmaAllocator_T(const VmaAllocatorCreateInfo* pCreateInfo) : + m_UseMutex((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT) == 0), + m_VulkanApiVersion(pCreateInfo->vulkanApiVersion != 0 ? pCreateInfo->vulkanApiVersion : VK_API_VERSION_1_0), + m_UseKhrDedicatedAllocation((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0), + m_UseKhrBindMemory2((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0), + m_UseExtMemoryBudget((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0), + m_UseAmdDeviceCoherentMemory((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT) != 0), + m_UseKhrBufferDeviceAddress((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT) != 0), + m_UseExtMemoryPriority((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT) != 0), + m_hDevice(pCreateInfo->device), + m_hInstance(pCreateInfo->instance), + m_AllocationCallbacksSpecified(pCreateInfo->pAllocationCallbacks != VMA_NULL), + m_AllocationCallbacks(pCreateInfo->pAllocationCallbacks ? + *pCreateInfo->pAllocationCallbacks : VmaEmptyAllocationCallbacks), + m_AllocationObjectAllocator(&m_AllocationCallbacks), + m_HeapSizeLimitMask(0), + m_DeviceMemoryCount(0), + m_PreferredLargeHeapBlockSize(0), + m_PhysicalDevice(pCreateInfo->physicalDevice), + m_GpuDefragmentationMemoryTypeBits(UINT32_MAX), + m_NextPoolId(0), + m_GlobalMemoryTypeBits(UINT32_MAX) +{ + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + m_UseKhrDedicatedAllocation = false; + m_UseKhrBindMemory2 = false; + } + + if(VMA_DEBUG_DETECT_CORRUPTION) + { + // Needs to be multiply of uint32_t size because we are going to write VMA_CORRUPTION_DETECTION_MAGIC_VALUE to it. + VMA_ASSERT(VMA_DEBUG_MARGIN % sizeof(uint32_t) == 0); + } + + VMA_ASSERT(pCreateInfo->physicalDevice && pCreateInfo->device && pCreateInfo->instance); + + if(m_VulkanApiVersion < VK_MAKE_VERSION(1, 1, 0)) + { +#if !(VMA_DEDICATED_ALLOCATION) + if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT) != 0) + { + VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT set but required extensions are disabled by preprocessor macros."); + } +#endif +#if !(VMA_BIND_MEMORY2) + if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT) != 0) + { + VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT set but required extension is disabled by preprocessor macros."); + } +#endif + } +#if !(VMA_MEMORY_BUDGET) + if((pCreateInfo->flags & VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT) != 0) + { + VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT set but required extension is disabled by preprocessor macros."); + } +#endif +#if !(VMA_BUFFER_DEVICE_ADDRESS) + if(m_UseKhrBufferDeviceAddress) + { + VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT is set but required extension or Vulkan 1.2 is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro."); + } +#endif +#if VMA_VULKAN_VERSION < 1003000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0)) + { + VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_3 but required Vulkan version is disabled by preprocessor macros."); + } +#endif +#if VMA_VULKAN_VERSION < 1002000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 2, 0)) + { + VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_2 but required Vulkan version is disabled by preprocessor macros."); + } +#endif +#if VMA_VULKAN_VERSION < 1001000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + VMA_ASSERT(0 && "vulkanApiVersion >= VK_API_VERSION_1_1 but required Vulkan version is disabled by preprocessor macros."); + } +#endif +#if !(VMA_MEMORY_PRIORITY) + if(m_UseExtMemoryPriority) + { + VMA_ASSERT(0 && "VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT is set but required extension is not available in your Vulkan header or its support in VMA has been disabled by a preprocessor macro."); + } +#endif + + memset(&m_DeviceMemoryCallbacks, 0 ,sizeof(m_DeviceMemoryCallbacks)); + memset(&m_PhysicalDeviceProperties, 0, sizeof(m_PhysicalDeviceProperties)); + memset(&m_MemProps, 0, sizeof(m_MemProps)); + + memset(&m_pBlockVectors, 0, sizeof(m_pBlockVectors)); + memset(&m_VulkanFunctions, 0, sizeof(m_VulkanFunctions)); + +#if VMA_EXTERNAL_MEMORY + memset(&m_TypeExternalMemoryHandleTypes, 0, sizeof(m_TypeExternalMemoryHandleTypes)); +#endif // #if VMA_EXTERNAL_MEMORY + + if(pCreateInfo->pDeviceMemoryCallbacks != VMA_NULL) + { + m_DeviceMemoryCallbacks.pUserData = pCreateInfo->pDeviceMemoryCallbacks->pUserData; + m_DeviceMemoryCallbacks.pfnAllocate = pCreateInfo->pDeviceMemoryCallbacks->pfnAllocate; + m_DeviceMemoryCallbacks.pfnFree = pCreateInfo->pDeviceMemoryCallbacks->pfnFree; + } + + ImportVulkanFunctions(pCreateInfo->pVulkanFunctions); + + (*m_VulkanFunctions.vkGetPhysicalDeviceProperties)(m_PhysicalDevice, &m_PhysicalDeviceProperties); + (*m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties)(m_PhysicalDevice, &m_MemProps); + + VMA_ASSERT(VmaIsPow2(VMA_MIN_ALIGNMENT)); + VMA_ASSERT(VmaIsPow2(VMA_DEBUG_MIN_BUFFER_IMAGE_GRANULARITY)); + VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.bufferImageGranularity)); + VMA_ASSERT(VmaIsPow2(m_PhysicalDeviceProperties.limits.nonCoherentAtomSize)); + + m_PreferredLargeHeapBlockSize = (pCreateInfo->preferredLargeHeapBlockSize != 0) ? + pCreateInfo->preferredLargeHeapBlockSize : static_cast(VMA_DEFAULT_LARGE_HEAP_BLOCK_SIZE); + + m_GlobalMemoryTypeBits = CalculateGlobalMemoryTypeBits(); + +#if VMA_EXTERNAL_MEMORY + if(pCreateInfo->pTypeExternalMemoryHandleTypes != VMA_NULL) + { + memcpy(m_TypeExternalMemoryHandleTypes, pCreateInfo->pTypeExternalMemoryHandleTypes, + sizeof(VkExternalMemoryHandleTypeFlagsKHR) * GetMemoryTypeCount()); + } +#endif // #if VMA_EXTERNAL_MEMORY + + if(pCreateInfo->pHeapSizeLimit != VMA_NULL) + { + for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex) + { + const VkDeviceSize limit = pCreateInfo->pHeapSizeLimit[heapIndex]; + if(limit != VK_WHOLE_SIZE) + { + m_HeapSizeLimitMask |= 1u << heapIndex; + if(limit < m_MemProps.memoryHeaps[heapIndex].size) + { + m_MemProps.memoryHeaps[heapIndex].size = limit; + } + } + } + } + + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + // Create only supported types + if((m_GlobalMemoryTypeBits & (1u << memTypeIndex)) != 0) + { + const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(memTypeIndex); + m_pBlockVectors[memTypeIndex] = vma_new(this, VmaBlockVector)( + this, + VK_NULL_HANDLE, // hParentPool + memTypeIndex, + preferredBlockSize, + 0, + SIZE_MAX, + GetBufferImageGranularity(), + false, // explicitBlockSize + 0, // algorithm + 0.5f, // priority (0.5 is the default per Vulkan spec) + GetMemoryTypeMinAlignment(memTypeIndex), // minAllocationAlignment + VMA_NULL); // // pMemoryAllocateNext + // No need to call m_pBlockVectors[memTypeIndex][blockVectorTypeIndex]->CreateMinBlocks here, + // becase minBlockCount is 0. + } + } +} + +VkResult VmaAllocator_T::Init(const VmaAllocatorCreateInfo* pCreateInfo) +{ + VkResult res = VK_SUCCESS; + +#if VMA_MEMORY_BUDGET + if(m_UseExtMemoryBudget) + { + UpdateVulkanBudget(); + } +#endif // #if VMA_MEMORY_BUDGET + + return res; +} + +VmaAllocator_T::~VmaAllocator_T() +{ + VMA_ASSERT(m_Pools.IsEmpty()); + + for(size_t memTypeIndex = GetMemoryTypeCount(); memTypeIndex--; ) + { + vma_delete(this, m_pBlockVectors[memTypeIndex]); + } +} + +void VmaAllocator_T::ImportVulkanFunctions(const VmaVulkanFunctions* pVulkanFunctions) +{ +#if VMA_STATIC_VULKAN_FUNCTIONS == 1 + ImportVulkanFunctions_Static(); +#endif + + if(pVulkanFunctions != VMA_NULL) + { + ImportVulkanFunctions_Custom(pVulkanFunctions); + } + +#if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1 + ImportVulkanFunctions_Dynamic(); +#endif + + ValidateVulkanFunctions(); +} + +#if VMA_STATIC_VULKAN_FUNCTIONS == 1 + +void VmaAllocator_T::ImportVulkanFunctions_Static() +{ + // Vulkan 1.0 + m_VulkanFunctions.vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)vkGetInstanceProcAddr; + m_VulkanFunctions.vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)vkGetDeviceProcAddr; + m_VulkanFunctions.vkGetPhysicalDeviceProperties = (PFN_vkGetPhysicalDeviceProperties)vkGetPhysicalDeviceProperties; + m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties = (PFN_vkGetPhysicalDeviceMemoryProperties)vkGetPhysicalDeviceMemoryProperties; + m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory; + m_VulkanFunctions.vkFreeMemory = (PFN_vkFreeMemory)vkFreeMemory; + m_VulkanFunctions.vkMapMemory = (PFN_vkMapMemory)vkMapMemory; + m_VulkanFunctions.vkUnmapMemory = (PFN_vkUnmapMemory)vkUnmapMemory; + m_VulkanFunctions.vkFlushMappedMemoryRanges = (PFN_vkFlushMappedMemoryRanges)vkFlushMappedMemoryRanges; + m_VulkanFunctions.vkInvalidateMappedMemoryRanges = (PFN_vkInvalidateMappedMemoryRanges)vkInvalidateMappedMemoryRanges; + m_VulkanFunctions.vkBindBufferMemory = (PFN_vkBindBufferMemory)vkBindBufferMemory; + m_VulkanFunctions.vkBindImageMemory = (PFN_vkBindImageMemory)vkBindImageMemory; + m_VulkanFunctions.vkGetBufferMemoryRequirements = (PFN_vkGetBufferMemoryRequirements)vkGetBufferMemoryRequirements; + m_VulkanFunctions.vkGetImageMemoryRequirements = (PFN_vkGetImageMemoryRequirements)vkGetImageMemoryRequirements; + m_VulkanFunctions.vkCreateBuffer = (PFN_vkCreateBuffer)vkCreateBuffer; + m_VulkanFunctions.vkDestroyBuffer = (PFN_vkDestroyBuffer)vkDestroyBuffer; + m_VulkanFunctions.vkCreateImage = (PFN_vkCreateImage)vkCreateImage; + m_VulkanFunctions.vkDestroyImage = (PFN_vkDestroyImage)vkDestroyImage; + m_VulkanFunctions.vkCmdCopyBuffer = (PFN_vkCmdCopyBuffer)vkCmdCopyBuffer; + + // Vulkan 1.1 +#if VMA_VULKAN_VERSION >= 1001000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR = (PFN_vkGetBufferMemoryRequirements2)vkGetBufferMemoryRequirements2; + m_VulkanFunctions.vkGetImageMemoryRequirements2KHR = (PFN_vkGetImageMemoryRequirements2)vkGetImageMemoryRequirements2; + m_VulkanFunctions.vkBindBufferMemory2KHR = (PFN_vkBindBufferMemory2)vkBindBufferMemory2; + m_VulkanFunctions.vkBindImageMemory2KHR = (PFN_vkBindImageMemory2)vkBindImageMemory2; + m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR = (PFN_vkGetPhysicalDeviceMemoryProperties2)vkGetPhysicalDeviceMemoryProperties2; + } +#endif + +#if VMA_VULKAN_VERSION >= 1003000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0)) + { + m_VulkanFunctions.vkGetDeviceBufferMemoryRequirements = (PFN_vkGetDeviceBufferMemoryRequirements)vkGetDeviceBufferMemoryRequirements; + m_VulkanFunctions.vkGetDeviceImageMemoryRequirements = (PFN_vkGetDeviceImageMemoryRequirements)vkGetDeviceImageMemoryRequirements; + } +#endif +} + +#endif // VMA_STATIC_VULKAN_FUNCTIONS == 1 + +void VmaAllocator_T::ImportVulkanFunctions_Custom(const VmaVulkanFunctions* pVulkanFunctions) +{ + VMA_ASSERT(pVulkanFunctions != VMA_NULL); + +#define VMA_COPY_IF_NOT_NULL(funcName) \ + if(pVulkanFunctions->funcName != VMA_NULL) m_VulkanFunctions.funcName = pVulkanFunctions->funcName; + + VMA_COPY_IF_NOT_NULL(vkGetInstanceProcAddr); + VMA_COPY_IF_NOT_NULL(vkGetDeviceProcAddr); + VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceProperties); + VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties); + VMA_COPY_IF_NOT_NULL(vkAllocateMemory); + VMA_COPY_IF_NOT_NULL(vkFreeMemory); + VMA_COPY_IF_NOT_NULL(vkMapMemory); + VMA_COPY_IF_NOT_NULL(vkUnmapMemory); + VMA_COPY_IF_NOT_NULL(vkFlushMappedMemoryRanges); + VMA_COPY_IF_NOT_NULL(vkInvalidateMappedMemoryRanges); + VMA_COPY_IF_NOT_NULL(vkBindBufferMemory); + VMA_COPY_IF_NOT_NULL(vkBindImageMemory); + VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements); + VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements); + VMA_COPY_IF_NOT_NULL(vkCreateBuffer); + VMA_COPY_IF_NOT_NULL(vkDestroyBuffer); + VMA_COPY_IF_NOT_NULL(vkCreateImage); + VMA_COPY_IF_NOT_NULL(vkDestroyImage); + VMA_COPY_IF_NOT_NULL(vkCmdCopyBuffer); + +#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + VMA_COPY_IF_NOT_NULL(vkGetBufferMemoryRequirements2KHR); + VMA_COPY_IF_NOT_NULL(vkGetImageMemoryRequirements2KHR); +#endif + +#if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000 + VMA_COPY_IF_NOT_NULL(vkBindBufferMemory2KHR); + VMA_COPY_IF_NOT_NULL(vkBindImageMemory2KHR); +#endif + +#if VMA_MEMORY_BUDGET + VMA_COPY_IF_NOT_NULL(vkGetPhysicalDeviceMemoryProperties2KHR); +#endif + +#if VMA_VULKAN_VERSION >= 1003000 + VMA_COPY_IF_NOT_NULL(vkGetDeviceBufferMemoryRequirements); + VMA_COPY_IF_NOT_NULL(vkGetDeviceImageMemoryRequirements); +#endif + +#undef VMA_COPY_IF_NOT_NULL +} + +#if VMA_DYNAMIC_VULKAN_FUNCTIONS == 1 + +void VmaAllocator_T::ImportVulkanFunctions_Dynamic() +{ + VMA_ASSERT(m_VulkanFunctions.vkGetInstanceProcAddr && m_VulkanFunctions.vkGetDeviceProcAddr && + "To use VMA_DYNAMIC_VULKAN_FUNCTIONS in new versions of VMA you now have to pass " + "VmaVulkanFunctions::vkGetInstanceProcAddr and vkGetDeviceProcAddr as VmaAllocatorCreateInfo::pVulkanFunctions. " + "Other members can be null."); + +#define VMA_FETCH_INSTANCE_FUNC(memberName, functionPointerType, functionNameString) \ + if(m_VulkanFunctions.memberName == VMA_NULL) \ + m_VulkanFunctions.memberName = \ + (functionPointerType)m_VulkanFunctions.vkGetInstanceProcAddr(m_hInstance, functionNameString); +#define VMA_FETCH_DEVICE_FUNC(memberName, functionPointerType, functionNameString) \ + if(m_VulkanFunctions.memberName == VMA_NULL) \ + m_VulkanFunctions.memberName = \ + (functionPointerType)m_VulkanFunctions.vkGetDeviceProcAddr(m_hDevice, functionNameString); + + VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceProperties, PFN_vkGetPhysicalDeviceProperties, "vkGetPhysicalDeviceProperties"); + VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties, PFN_vkGetPhysicalDeviceMemoryProperties, "vkGetPhysicalDeviceMemoryProperties"); + VMA_FETCH_DEVICE_FUNC(vkAllocateMemory, PFN_vkAllocateMemory, "vkAllocateMemory"); + VMA_FETCH_DEVICE_FUNC(vkFreeMemory, PFN_vkFreeMemory, "vkFreeMemory"); + VMA_FETCH_DEVICE_FUNC(vkMapMemory, PFN_vkMapMemory, "vkMapMemory"); + VMA_FETCH_DEVICE_FUNC(vkUnmapMemory, PFN_vkUnmapMemory, "vkUnmapMemory"); + VMA_FETCH_DEVICE_FUNC(vkFlushMappedMemoryRanges, PFN_vkFlushMappedMemoryRanges, "vkFlushMappedMemoryRanges"); + VMA_FETCH_DEVICE_FUNC(vkInvalidateMappedMemoryRanges, PFN_vkInvalidateMappedMemoryRanges, "vkInvalidateMappedMemoryRanges"); + VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory, PFN_vkBindBufferMemory, "vkBindBufferMemory"); + VMA_FETCH_DEVICE_FUNC(vkBindImageMemory, PFN_vkBindImageMemory, "vkBindImageMemory"); + VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements, PFN_vkGetBufferMemoryRequirements, "vkGetBufferMemoryRequirements"); + VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements, PFN_vkGetImageMemoryRequirements, "vkGetImageMemoryRequirements"); + VMA_FETCH_DEVICE_FUNC(vkCreateBuffer, PFN_vkCreateBuffer, "vkCreateBuffer"); + VMA_FETCH_DEVICE_FUNC(vkDestroyBuffer, PFN_vkDestroyBuffer, "vkDestroyBuffer"); + VMA_FETCH_DEVICE_FUNC(vkCreateImage, PFN_vkCreateImage, "vkCreateImage"); + VMA_FETCH_DEVICE_FUNC(vkDestroyImage, PFN_vkDestroyImage, "vkDestroyImage"); + VMA_FETCH_DEVICE_FUNC(vkCmdCopyBuffer, PFN_vkCmdCopyBuffer, "vkCmdCopyBuffer"); + +#if VMA_VULKAN_VERSION >= 1001000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2, "vkGetBufferMemoryRequirements2"); + VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2, "vkGetImageMemoryRequirements2"); + VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2, "vkBindBufferMemory2"); + VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2, "vkBindImageMemory2"); + VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2, "vkGetPhysicalDeviceMemoryProperties2"); + } +#endif + +#if VMA_DEDICATED_ALLOCATION + if(m_UseKhrDedicatedAllocation) + { + VMA_FETCH_DEVICE_FUNC(vkGetBufferMemoryRequirements2KHR, PFN_vkGetBufferMemoryRequirements2KHR, "vkGetBufferMemoryRequirements2KHR"); + VMA_FETCH_DEVICE_FUNC(vkGetImageMemoryRequirements2KHR, PFN_vkGetImageMemoryRequirements2KHR, "vkGetImageMemoryRequirements2KHR"); + } +#endif + +#if VMA_BIND_MEMORY2 + if(m_UseKhrBindMemory2) + { + VMA_FETCH_DEVICE_FUNC(vkBindBufferMemory2KHR, PFN_vkBindBufferMemory2KHR, "vkBindBufferMemory2KHR"); + VMA_FETCH_DEVICE_FUNC(vkBindImageMemory2KHR, PFN_vkBindImageMemory2KHR, "vkBindImageMemory2KHR"); + } +#endif // #if VMA_BIND_MEMORY2 + +#if VMA_MEMORY_BUDGET + if(m_UseExtMemoryBudget) + { + VMA_FETCH_INSTANCE_FUNC(vkGetPhysicalDeviceMemoryProperties2KHR, PFN_vkGetPhysicalDeviceMemoryProperties2KHR, "vkGetPhysicalDeviceMemoryProperties2KHR"); + } +#endif // #if VMA_MEMORY_BUDGET + +#if VMA_VULKAN_VERSION >= 1003000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0)) + { + VMA_FETCH_DEVICE_FUNC(vkGetDeviceBufferMemoryRequirements, PFN_vkGetDeviceBufferMemoryRequirements, "vkGetDeviceBufferMemoryRequirements"); + VMA_FETCH_DEVICE_FUNC(vkGetDeviceImageMemoryRequirements, PFN_vkGetDeviceImageMemoryRequirements, "vkGetDeviceImageMemoryRequirements"); + } +#endif + +#undef VMA_FETCH_DEVICE_FUNC +#undef VMA_FETCH_INSTANCE_FUNC +} + +#endif // VMA_DYNAMIC_VULKAN_FUNCTIONS == 1 + +void VmaAllocator_T::ValidateVulkanFunctions() +{ + VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceProperties != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkAllocateMemory != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkFreeMemory != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkMapMemory != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkUnmapMemory != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkFlushMappedMemoryRanges != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkInvalidateMappedMemoryRanges != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkCreateBuffer != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkDestroyBuffer != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkCreateImage != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkDestroyImage != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkCmdCopyBuffer != VMA_NULL); + +#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrDedicatedAllocation) + { + VMA_ASSERT(m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkGetImageMemoryRequirements2KHR != VMA_NULL); + } +#endif + +#if VMA_BIND_MEMORY2 || VMA_VULKAN_VERSION >= 1001000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0) || m_UseKhrBindMemory2) + { + VMA_ASSERT(m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL); + } +#endif + +#if VMA_MEMORY_BUDGET || VMA_VULKAN_VERSION >= 1001000 + if(m_UseExtMemoryBudget || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + VMA_ASSERT(m_VulkanFunctions.vkGetPhysicalDeviceMemoryProperties2KHR != VMA_NULL); + } +#endif + +#if VMA_VULKAN_VERSION >= 1003000 + if(m_VulkanApiVersion >= VK_MAKE_VERSION(1, 3, 0)) + { + VMA_ASSERT(m_VulkanFunctions.vkGetDeviceBufferMemoryRequirements != VMA_NULL); + VMA_ASSERT(m_VulkanFunctions.vkGetDeviceImageMemoryRequirements != VMA_NULL); + } +#endif +} + +VkDeviceSize VmaAllocator_T::CalcPreferredBlockSize(uint32_t memTypeIndex) +{ + const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex); + const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size; + const bool isSmallHeap = heapSize <= VMA_SMALL_HEAP_MAX_SIZE; + return VmaAlignUp(isSmallHeap ? (heapSize / 8) : m_PreferredLargeHeapBlockSize, (VkDeviceSize)32); +} + +VkResult VmaAllocator_T::AllocateMemoryOfType( + VmaPool pool, + VkDeviceSize size, + VkDeviceSize alignment, + bool dedicatedPreferred, + VkBuffer dedicatedBuffer, + VkImage dedicatedImage, + VkFlags dedicatedBufferImageUsage, + const VmaAllocationCreateInfo& createInfo, + uint32_t memTypeIndex, + VmaSuballocationType suballocType, + VmaDedicatedAllocationList& dedicatedAllocations, + VmaBlockVector& blockVector, + size_t allocationCount, + VmaAllocation* pAllocations) +{ + VMA_ASSERT(pAllocations != VMA_NULL); + VMA_DEBUG_LOG_FORMAT(" AllocateMemory: MemoryTypeIndex=%u, AllocationCount=%zu, Size=%llu", memTypeIndex, allocationCount, size); + + VmaAllocationCreateInfo finalCreateInfo = createInfo; + VkResult res = CalcMemTypeParams( + finalCreateInfo, + memTypeIndex, + size, + allocationCount); + if(res != VK_SUCCESS) + return res; + + if((finalCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0) + { + return AllocateDedicatedMemory( + pool, + size, + suballocType, + dedicatedAllocations, + memTypeIndex, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0, + (finalCreateInfo.flags & + (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0, + finalCreateInfo.pUserData, + finalCreateInfo.priority, + dedicatedBuffer, + dedicatedImage, + dedicatedBufferImageUsage, + allocationCount, + pAllocations, + blockVector.GetAllocationNextPtr()); + } + else + { + const bool canAllocateDedicated = + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) == 0 && + (pool == VK_NULL_HANDLE || !blockVector.HasExplicitBlockSize()); + + if(canAllocateDedicated) + { + // Heuristics: Allocate dedicated memory if requested size if greater than half of preferred block size. + if(size > blockVector.GetPreferredBlockSize() / 2) + { + dedicatedPreferred = true; + } + // Protection against creating each allocation as dedicated when we reach or exceed heap size/budget, + // which can quickly deplete maxMemoryAllocationCount: Don't prefer dedicated allocations when above + // 3/4 of the maximum allocation count. + if(m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount < UINT32_MAX / 4 && + m_DeviceMemoryCount.load() > m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount * 3 / 4) + { + dedicatedPreferred = false; + } + + if(dedicatedPreferred) + { + res = AllocateDedicatedMemory( + pool, + size, + suballocType, + dedicatedAllocations, + memTypeIndex, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0, + (finalCreateInfo.flags & + (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0, + finalCreateInfo.pUserData, + finalCreateInfo.priority, + dedicatedBuffer, + dedicatedImage, + dedicatedBufferImageUsage, + allocationCount, + pAllocations, + blockVector.GetAllocationNextPtr()); + if(res == VK_SUCCESS) + { + // Succeeded: AllocateDedicatedMemory function already filled pMemory, nothing more to do here. + VMA_DEBUG_LOG(" Allocated as DedicatedMemory"); + return VK_SUCCESS; + } + } + } + + res = blockVector.Allocate( + size, + alignment, + finalCreateInfo, + suballocType, + allocationCount, + pAllocations); + if(res == VK_SUCCESS) + return VK_SUCCESS; + + // Try dedicated memory. + if(canAllocateDedicated && !dedicatedPreferred) + { + res = AllocateDedicatedMemory( + pool, + size, + suballocType, + dedicatedAllocations, + memTypeIndex, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_USER_DATA_COPY_STRING_BIT) != 0, + (finalCreateInfo.flags & + (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0, + (finalCreateInfo.flags & VMA_ALLOCATION_CREATE_CAN_ALIAS_BIT) != 0, + finalCreateInfo.pUserData, + finalCreateInfo.priority, + dedicatedBuffer, + dedicatedImage, + dedicatedBufferImageUsage, + allocationCount, + pAllocations, + blockVector.GetAllocationNextPtr()); + if(res == VK_SUCCESS) + { + // Succeeded: AllocateDedicatedMemory function already filled pMemory, nothing more to do here. + VMA_DEBUG_LOG(" Allocated as DedicatedMemory"); + return VK_SUCCESS; + } + } + // Everything failed: Return error code. + VMA_DEBUG_LOG(" vkAllocateMemory FAILED"); + return res; + } +} + +VkResult VmaAllocator_T::AllocateDedicatedMemory( + VmaPool pool, + VkDeviceSize size, + VmaSuballocationType suballocType, + VmaDedicatedAllocationList& dedicatedAllocations, + uint32_t memTypeIndex, + bool map, + bool isUserDataString, + bool isMappingAllowed, + bool canAliasMemory, + void* pUserData, + float priority, + VkBuffer dedicatedBuffer, + VkImage dedicatedImage, + VkFlags dedicatedBufferImageUsage, + size_t allocationCount, + VmaAllocation* pAllocations, + const void* pNextChain) +{ + VMA_ASSERT(allocationCount > 0 && pAllocations); + + VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; + allocInfo.memoryTypeIndex = memTypeIndex; + allocInfo.allocationSize = size; + allocInfo.pNext = pNextChain; + +#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + VkMemoryDedicatedAllocateInfoKHR dedicatedAllocInfo = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR }; + if(!canAliasMemory) + { + if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + if(dedicatedBuffer != VK_NULL_HANDLE) + { + VMA_ASSERT(dedicatedImage == VK_NULL_HANDLE); + dedicatedAllocInfo.buffer = dedicatedBuffer; + VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo); + } + else if(dedicatedImage != VK_NULL_HANDLE) + { + dedicatedAllocInfo.image = dedicatedImage; + VmaPnextChainPushFront(&allocInfo, &dedicatedAllocInfo); + } + } + } +#endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + +#if VMA_BUFFER_DEVICE_ADDRESS + VkMemoryAllocateFlagsInfoKHR allocFlagsInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR }; + if(m_UseKhrBufferDeviceAddress) + { + bool canContainBufferWithDeviceAddress = true; + if(dedicatedBuffer != VK_NULL_HANDLE) + { + canContainBufferWithDeviceAddress = dedicatedBufferImageUsage == UINT32_MAX || // Usage flags unknown + (dedicatedBufferImageUsage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_EXT) != 0; + } + else if(dedicatedImage != VK_NULL_HANDLE) + { + canContainBufferWithDeviceAddress = false; + } + if(canContainBufferWithDeviceAddress) + { + allocFlagsInfo.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR; + VmaPnextChainPushFront(&allocInfo, &allocFlagsInfo); + } + } +#endif // #if VMA_BUFFER_DEVICE_ADDRESS + +#if VMA_MEMORY_PRIORITY + VkMemoryPriorityAllocateInfoEXT priorityInfo = { VK_STRUCTURE_TYPE_MEMORY_PRIORITY_ALLOCATE_INFO_EXT }; + if(m_UseExtMemoryPriority) + { + VMA_ASSERT(priority >= 0.f && priority <= 1.f); + priorityInfo.priority = priority; + VmaPnextChainPushFront(&allocInfo, &priorityInfo); + } +#endif // #if VMA_MEMORY_PRIORITY + +#if VMA_EXTERNAL_MEMORY + // Attach VkExportMemoryAllocateInfoKHR if necessary. + VkExportMemoryAllocateInfoKHR exportMemoryAllocInfo = { VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO_KHR }; + exportMemoryAllocInfo.handleTypes = GetExternalMemoryHandleTypeFlags(memTypeIndex); + if(exportMemoryAllocInfo.handleTypes != 0) + { + VmaPnextChainPushFront(&allocInfo, &exportMemoryAllocInfo); + } +#endif // #if VMA_EXTERNAL_MEMORY + + size_t allocIndex; + VkResult res = VK_SUCCESS; + for(allocIndex = 0; allocIndex < allocationCount; ++allocIndex) + { + res = AllocateDedicatedMemoryPage( + pool, + size, + suballocType, + memTypeIndex, + allocInfo, + map, + isUserDataString, + isMappingAllowed, + pUserData, + pAllocations + allocIndex); + if(res != VK_SUCCESS) + { + break; + } + } + + if(res == VK_SUCCESS) + { + for (allocIndex = 0; allocIndex < allocationCount; ++allocIndex) + { + dedicatedAllocations.Register(pAllocations[allocIndex]); + } + VMA_DEBUG_LOG_FORMAT(" Allocated DedicatedMemory Count=%zu, MemoryTypeIndex=#%u", allocationCount, memTypeIndex); + } + else + { + // Free all already created allocations. + while(allocIndex--) + { + VmaAllocation currAlloc = pAllocations[allocIndex]; + VkDeviceMemory hMemory = currAlloc->GetMemory(); + + /* + There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory + before vkFreeMemory. + + if(currAlloc->GetMappedData() != VMA_NULL) + { + (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory); + } + */ + + FreeVulkanMemory(memTypeIndex, currAlloc->GetSize(), hMemory); + m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), currAlloc->GetSize()); + m_AllocationObjectAllocator.Free(currAlloc); + } + + memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount); + } + + return res; +} + +VkResult VmaAllocator_T::AllocateDedicatedMemoryPage( + VmaPool pool, + VkDeviceSize size, + VmaSuballocationType suballocType, + uint32_t memTypeIndex, + const VkMemoryAllocateInfo& allocInfo, + bool map, + bool isUserDataString, + bool isMappingAllowed, + void* pUserData, + VmaAllocation* pAllocation) +{ + VkDeviceMemory hMemory = VK_NULL_HANDLE; + VkResult res = AllocateVulkanMemory(&allocInfo, &hMemory); + if(res < 0) + { + VMA_DEBUG_LOG(" vkAllocateMemory FAILED"); + return res; + } + + void* pMappedData = VMA_NULL; + if(map) + { + res = (*m_VulkanFunctions.vkMapMemory)( + m_hDevice, + hMemory, + 0, + VK_WHOLE_SIZE, + 0, + &pMappedData); + if(res < 0) + { + VMA_DEBUG_LOG(" vkMapMemory FAILED"); + FreeVulkanMemory(memTypeIndex, size, hMemory); + return res; + } + } + + *pAllocation = m_AllocationObjectAllocator.Allocate(isMappingAllowed); + (*pAllocation)->InitDedicatedAllocation(pool, memTypeIndex, hMemory, suballocType, pMappedData, size); + if (isUserDataString) + (*pAllocation)->SetName(this, (const char*)pUserData); + else + (*pAllocation)->SetUserData(this, pUserData); + m_Budget.AddAllocation(MemoryTypeIndexToHeapIndex(memTypeIndex), size); + if(VMA_DEBUG_INITIALIZE_ALLOCATIONS) + { + FillAllocation(*pAllocation, VMA_ALLOCATION_FILL_PATTERN_CREATED); + } + + return VK_SUCCESS; +} + +void VmaAllocator_T::GetBufferMemoryRequirements( + VkBuffer hBuffer, + VkMemoryRequirements& memReq, + bool& requiresDedicatedAllocation, + bool& prefersDedicatedAllocation) const +{ +#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + VkBufferMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_BUFFER_MEMORY_REQUIREMENTS_INFO_2_KHR }; + memReqInfo.buffer = hBuffer; + + VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR }; + + VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR }; + VmaPnextChainPushFront(&memReq2, &memDedicatedReq); + + (*m_VulkanFunctions.vkGetBufferMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2); + + memReq = memReq2.memoryRequirements; + requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE); + prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE); + } + else +#endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + { + (*m_VulkanFunctions.vkGetBufferMemoryRequirements)(m_hDevice, hBuffer, &memReq); + requiresDedicatedAllocation = false; + prefersDedicatedAllocation = false; + } +} + +void VmaAllocator_T::GetImageMemoryRequirements( + VkImage hImage, + VkMemoryRequirements& memReq, + bool& requiresDedicatedAllocation, + bool& prefersDedicatedAllocation) const +{ +#if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + if(m_UseKhrDedicatedAllocation || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) + { + VkImageMemoryRequirementsInfo2KHR memReqInfo = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_REQUIREMENTS_INFO_2_KHR }; + memReqInfo.image = hImage; + + VkMemoryDedicatedRequirementsKHR memDedicatedReq = { VK_STRUCTURE_TYPE_MEMORY_DEDICATED_REQUIREMENTS_KHR }; + + VkMemoryRequirements2KHR memReq2 = { VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2_KHR }; + VmaPnextChainPushFront(&memReq2, &memDedicatedReq); + + (*m_VulkanFunctions.vkGetImageMemoryRequirements2KHR)(m_hDevice, &memReqInfo, &memReq2); + + memReq = memReq2.memoryRequirements; + requiresDedicatedAllocation = (memDedicatedReq.requiresDedicatedAllocation != VK_FALSE); + prefersDedicatedAllocation = (memDedicatedReq.prefersDedicatedAllocation != VK_FALSE); + } + else +#endif // #if VMA_DEDICATED_ALLOCATION || VMA_VULKAN_VERSION >= 1001000 + { + (*m_VulkanFunctions.vkGetImageMemoryRequirements)(m_hDevice, hImage, &memReq); + requiresDedicatedAllocation = false; + prefersDedicatedAllocation = false; + } +} + +VkResult VmaAllocator_T::FindMemoryTypeIndex( + uint32_t memoryTypeBits, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + VkFlags bufImgUsage, + uint32_t* pMemoryTypeIndex) const +{ + memoryTypeBits &= GetGlobalMemoryTypeBits(); + + if(pAllocationCreateInfo->memoryTypeBits != 0) + { + memoryTypeBits &= pAllocationCreateInfo->memoryTypeBits; + } + + VkMemoryPropertyFlags requiredFlags = 0, preferredFlags = 0, notPreferredFlags = 0; + if(!FindMemoryPreferences( + IsIntegratedGpu(), + *pAllocationCreateInfo, + bufImgUsage, + requiredFlags, preferredFlags, notPreferredFlags)) + { + return VK_ERROR_FEATURE_NOT_PRESENT; + } + + *pMemoryTypeIndex = UINT32_MAX; + uint32_t minCost = UINT32_MAX; + for(uint32_t memTypeIndex = 0, memTypeBit = 1; + memTypeIndex < GetMemoryTypeCount(); + ++memTypeIndex, memTypeBit <<= 1) + { + // This memory type is acceptable according to memoryTypeBits bitmask. + if((memTypeBit & memoryTypeBits) != 0) + { + const VkMemoryPropertyFlags currFlags = + m_MemProps.memoryTypes[memTypeIndex].propertyFlags; + // This memory type contains requiredFlags. + if((requiredFlags & ~currFlags) == 0) + { + // Calculate cost as number of bits from preferredFlags not present in this memory type. + uint32_t currCost = VMA_COUNT_BITS_SET(preferredFlags & ~currFlags) + + VMA_COUNT_BITS_SET(currFlags & notPreferredFlags); + // Remember memory type with lowest cost. + if(currCost < minCost) + { + *pMemoryTypeIndex = memTypeIndex; + if(currCost == 0) + { + return VK_SUCCESS; + } + minCost = currCost; + } + } + } + } + return (*pMemoryTypeIndex != UINT32_MAX) ? VK_SUCCESS : VK_ERROR_FEATURE_NOT_PRESENT; +} + +VkResult VmaAllocator_T::CalcMemTypeParams( + VmaAllocationCreateInfo& inoutCreateInfo, + uint32_t memTypeIndex, + VkDeviceSize size, + size_t allocationCount) +{ + // If memory type is not HOST_VISIBLE, disable MAPPED. + if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0 && + (m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) == 0) + { + inoutCreateInfo.flags &= ~VMA_ALLOCATION_CREATE_MAPPED_BIT; + } + + if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 && + (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT) != 0) + { + const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memTypeIndex); + VmaBudget heapBudget = {}; + GetHeapBudgets(&heapBudget, heapIndex, 1); + if(heapBudget.usage + size * allocationCount > heapBudget.budget) + { + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + } + return VK_SUCCESS; +} + +VkResult VmaAllocator_T::CalcAllocationParams( + VmaAllocationCreateInfo& inoutCreateInfo, + bool dedicatedRequired, + bool dedicatedPreferred) +{ + VMA_ASSERT((inoutCreateInfo.flags & + (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != + (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT) && + "Specifying both flags VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT and VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT is incorrect."); + VMA_ASSERT((((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT) == 0 || + (inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0)) && + "Specifying VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT requires also VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT."); + if(inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO || inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE || inoutCreateInfo.usage == VMA_MEMORY_USAGE_AUTO_PREFER_HOST) + { + if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_MAPPED_BIT) != 0) + { + VMA_ASSERT((inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) != 0 && + "When using VMA_ALLOCATION_CREATE_MAPPED_BIT and usage = VMA_MEMORY_USAGE_AUTO*, you must also specify VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT."); + } + } + + // If memory is lazily allocated, it should be always dedicated. + if(dedicatedRequired || + inoutCreateInfo.usage == VMA_MEMORY_USAGE_GPU_LAZILY_ALLOCATED) + { + inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; + } + + if(inoutCreateInfo.pool != VK_NULL_HANDLE) + { + if(inoutCreateInfo.pool->m_BlockVector.HasExplicitBlockSize() && + (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0) + { + VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT while current custom pool doesn't support dedicated allocations."); + return VK_ERROR_FEATURE_NOT_PRESENT; + } + inoutCreateInfo.priority = inoutCreateInfo.pool->m_BlockVector.GetPriority(); + } + + if((inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT) != 0 && + (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0) + { + VMA_ASSERT(0 && "Specifying VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT together with VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT makes no sense."); + return VK_ERROR_FEATURE_NOT_PRESENT; + } + + if(VMA_DEBUG_ALWAYS_DEDICATED_MEMORY && + (inoutCreateInfo.flags & VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT) != 0) + { + inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; + } + + // Non-auto USAGE values imply HOST_ACCESS flags. + // And so does VMA_MEMORY_USAGE_UNKNOWN because it is used with custom pools. + // Which specific flag is used doesn't matter. They change things only when used with VMA_MEMORY_USAGE_AUTO*. + // Otherwise they just protect from assert on mapping. + if(inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO && + inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE && + inoutCreateInfo.usage != VMA_MEMORY_USAGE_AUTO_PREFER_HOST) + { + if((inoutCreateInfo.flags & (VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT)) == 0) + { + inoutCreateInfo.flags |= VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT; + } + } + + return VK_SUCCESS; +} + +VkResult VmaAllocator_T::AllocateMemory( + const VkMemoryRequirements& vkMemReq, + bool requiresDedicatedAllocation, + bool prefersDedicatedAllocation, + VkBuffer dedicatedBuffer, + VkImage dedicatedImage, + VkFlags dedicatedBufferImageUsage, + const VmaAllocationCreateInfo& createInfo, + VmaSuballocationType suballocType, + size_t allocationCount, + VmaAllocation* pAllocations) +{ + memset(pAllocations, 0, sizeof(VmaAllocation) * allocationCount); + + VMA_ASSERT(VmaIsPow2(vkMemReq.alignment)); + + if(vkMemReq.size == 0) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + + VmaAllocationCreateInfo createInfoFinal = createInfo; + VkResult res = CalcAllocationParams(createInfoFinal, requiresDedicatedAllocation, prefersDedicatedAllocation); + if(res != VK_SUCCESS) + return res; + + if(createInfoFinal.pool != VK_NULL_HANDLE) + { + VmaBlockVector& blockVector = createInfoFinal.pool->m_BlockVector; + return AllocateMemoryOfType( + createInfoFinal.pool, + vkMemReq.size, + vkMemReq.alignment, + prefersDedicatedAllocation, + dedicatedBuffer, + dedicatedImage, + dedicatedBufferImageUsage, + createInfoFinal, + blockVector.GetMemoryTypeIndex(), + suballocType, + createInfoFinal.pool->m_DedicatedAllocations, + blockVector, + allocationCount, + pAllocations); + } + else + { + // Bit mask of memory Vulkan types acceptable for this allocation. + uint32_t memoryTypeBits = vkMemReq.memoryTypeBits; + uint32_t memTypeIndex = UINT32_MAX; + res = FindMemoryTypeIndex(memoryTypeBits, &createInfoFinal, dedicatedBufferImageUsage, &memTypeIndex); + // Can't find any single memory type matching requirements. res is VK_ERROR_FEATURE_NOT_PRESENT. + if(res != VK_SUCCESS) + return res; + do + { + VmaBlockVector* blockVector = m_pBlockVectors[memTypeIndex]; + VMA_ASSERT(blockVector && "Trying to use unsupported memory type!"); + res = AllocateMemoryOfType( + VK_NULL_HANDLE, + vkMemReq.size, + vkMemReq.alignment, + requiresDedicatedAllocation || prefersDedicatedAllocation, + dedicatedBuffer, + dedicatedImage, + dedicatedBufferImageUsage, + createInfoFinal, + memTypeIndex, + suballocType, + m_DedicatedAllocations[memTypeIndex], + *blockVector, + allocationCount, + pAllocations); + // Allocation succeeded + if(res == VK_SUCCESS) + return VK_SUCCESS; + + // Remove old memTypeIndex from list of possibilities. + memoryTypeBits &= ~(1u << memTypeIndex); + // Find alternative memTypeIndex. + res = FindMemoryTypeIndex(memoryTypeBits, &createInfoFinal, dedicatedBufferImageUsage, &memTypeIndex); + } while(res == VK_SUCCESS); + + // No other matching memory type index could be found. + // Not returning res, which is VK_ERROR_FEATURE_NOT_PRESENT, because we already failed to allocate once. + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } +} + +void VmaAllocator_T::FreeMemory( + size_t allocationCount, + const VmaAllocation* pAllocations) +{ + VMA_ASSERT(pAllocations); + + for(size_t allocIndex = allocationCount; allocIndex--; ) + { + VmaAllocation allocation = pAllocations[allocIndex]; + + if(allocation != VK_NULL_HANDLE) + { + if(VMA_DEBUG_INITIALIZE_ALLOCATIONS) + { + FillAllocation(allocation, VMA_ALLOCATION_FILL_PATTERN_DESTROYED); + } + + allocation->FreeName(this); + + switch(allocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + VmaBlockVector* pBlockVector = VMA_NULL; + VmaPool hPool = allocation->GetParentPool(); + if(hPool != VK_NULL_HANDLE) + { + pBlockVector = &hPool->m_BlockVector; + } + else + { + const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); + pBlockVector = m_pBlockVectors[memTypeIndex]; + VMA_ASSERT(pBlockVector && "Trying to free memory of unsupported type!"); + } + pBlockVector->Free(allocation); + } + break; + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + FreeDedicatedMemory(allocation); + break; + default: + VMA_ASSERT(0); + } + } + } +} + +void VmaAllocator_T::CalculateStatistics(VmaTotalStatistics* pStats) +{ + // Initialize. + VmaClearDetailedStatistics(pStats->total); + for(uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; ++i) + VmaClearDetailedStatistics(pStats->memoryType[i]); + for(uint32_t i = 0; i < VK_MAX_MEMORY_HEAPS; ++i) + VmaClearDetailedStatistics(pStats->memoryHeap[i]); + + // Process default pools. + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex]; + if (pBlockVector != VMA_NULL) + pBlockVector->AddDetailedStatistics(pStats->memoryType[memTypeIndex]); + } + + // Process custom pools. + { + VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex); + for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool)) + { + VmaBlockVector& blockVector = pool->m_BlockVector; + const uint32_t memTypeIndex = blockVector.GetMemoryTypeIndex(); + blockVector.AddDetailedStatistics(pStats->memoryType[memTypeIndex]); + pool->m_DedicatedAllocations.AddDetailedStatistics(pStats->memoryType[memTypeIndex]); + } + } + + // Process dedicated allocations. + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + m_DedicatedAllocations[memTypeIndex].AddDetailedStatistics(pStats->memoryType[memTypeIndex]); + } + + // Sum from memory types to memory heaps. + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + const uint32_t memHeapIndex = m_MemProps.memoryTypes[memTypeIndex].heapIndex; + VmaAddDetailedStatistics(pStats->memoryHeap[memHeapIndex], pStats->memoryType[memTypeIndex]); + } + + // Sum from memory heaps to total. + for(uint32_t memHeapIndex = 0; memHeapIndex < GetMemoryHeapCount(); ++memHeapIndex) + VmaAddDetailedStatistics(pStats->total, pStats->memoryHeap[memHeapIndex]); + + VMA_ASSERT(pStats->total.statistics.allocationCount == 0 || + pStats->total.allocationSizeMax >= pStats->total.allocationSizeMin); + VMA_ASSERT(pStats->total.unusedRangeCount == 0 || + pStats->total.unusedRangeSizeMax >= pStats->total.unusedRangeSizeMin); +} + +void VmaAllocator_T::GetHeapBudgets(VmaBudget* outBudgets, uint32_t firstHeap, uint32_t heapCount) +{ +#if VMA_MEMORY_BUDGET + if(m_UseExtMemoryBudget) + { + if(m_Budget.m_OperationsSinceBudgetFetch < 30) + { + VmaMutexLockRead lockRead(m_Budget.m_BudgetMutex, m_UseMutex); + for(uint32_t i = 0; i < heapCount; ++i, ++outBudgets) + { + const uint32_t heapIndex = firstHeap + i; + + outBudgets->statistics.blockCount = m_Budget.m_BlockCount[heapIndex]; + outBudgets->statistics.allocationCount = m_Budget.m_AllocationCount[heapIndex]; + outBudgets->statistics.blockBytes = m_Budget.m_BlockBytes[heapIndex]; + outBudgets->statistics.allocationBytes = m_Budget.m_AllocationBytes[heapIndex]; + + if(m_Budget.m_VulkanUsage[heapIndex] + outBudgets->statistics.blockBytes > m_Budget.m_BlockBytesAtBudgetFetch[heapIndex]) + { + outBudgets->usage = m_Budget.m_VulkanUsage[heapIndex] + + outBudgets->statistics.blockBytes - m_Budget.m_BlockBytesAtBudgetFetch[heapIndex]; + } + else + { + outBudgets->usage = 0; + } + + // Have to take MIN with heap size because explicit HeapSizeLimit is included in it. + outBudgets->budget = VMA_MIN( + m_Budget.m_VulkanBudget[heapIndex], m_MemProps.memoryHeaps[heapIndex].size); + } + } + else + { + UpdateVulkanBudget(); // Outside of mutex lock + GetHeapBudgets(outBudgets, firstHeap, heapCount); // Recursion + } + } + else +#endif + { + for(uint32_t i = 0; i < heapCount; ++i, ++outBudgets) + { + const uint32_t heapIndex = firstHeap + i; + + outBudgets->statistics.blockCount = m_Budget.m_BlockCount[heapIndex]; + outBudgets->statistics.allocationCount = m_Budget.m_AllocationCount[heapIndex]; + outBudgets->statistics.blockBytes = m_Budget.m_BlockBytes[heapIndex]; + outBudgets->statistics.allocationBytes = m_Budget.m_AllocationBytes[heapIndex]; + + outBudgets->usage = outBudgets->statistics.blockBytes; + outBudgets->budget = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics. + } + } +} + +void VmaAllocator_T::GetAllocationInfo(VmaAllocation hAllocation, VmaAllocationInfo* pAllocationInfo) +{ + pAllocationInfo->memoryType = hAllocation->GetMemoryTypeIndex(); + pAllocationInfo->deviceMemory = hAllocation->GetMemory(); + pAllocationInfo->offset = hAllocation->GetOffset(); + pAllocationInfo->size = hAllocation->GetSize(); + pAllocationInfo->pMappedData = hAllocation->GetMappedData(); + pAllocationInfo->pUserData = hAllocation->GetUserData(); + pAllocationInfo->pName = hAllocation->GetName(); +} + +VkResult VmaAllocator_T::CreatePool(const VmaPoolCreateInfo* pCreateInfo, VmaPool* pPool) +{ + VMA_DEBUG_LOG_FORMAT(" CreatePool: MemoryTypeIndex=%u, flags=%u", pCreateInfo->memoryTypeIndex, pCreateInfo->flags); + + VmaPoolCreateInfo newCreateInfo = *pCreateInfo; + + // Protection against uninitialized new structure member. If garbage data are left there, this pointer dereference would crash. + if(pCreateInfo->pMemoryAllocateNext) + { + VMA_ASSERT(((const VkBaseInStructure*)pCreateInfo->pMemoryAllocateNext)->sType != 0); + } + + if(newCreateInfo.maxBlockCount == 0) + { + newCreateInfo.maxBlockCount = SIZE_MAX; + } + if(newCreateInfo.minBlockCount > newCreateInfo.maxBlockCount) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + // Memory type index out of range or forbidden. + if(pCreateInfo->memoryTypeIndex >= GetMemoryTypeCount() || + ((1u << pCreateInfo->memoryTypeIndex) & m_GlobalMemoryTypeBits) == 0) + { + return VK_ERROR_FEATURE_NOT_PRESENT; + } + if(newCreateInfo.minAllocationAlignment > 0) + { + VMA_ASSERT(VmaIsPow2(newCreateInfo.minAllocationAlignment)); + } + + const VkDeviceSize preferredBlockSize = CalcPreferredBlockSize(newCreateInfo.memoryTypeIndex); + + *pPool = vma_new(this, VmaPool_T)(this, newCreateInfo, preferredBlockSize); + + VkResult res = (*pPool)->m_BlockVector.CreateMinBlocks(); + if(res != VK_SUCCESS) + { + vma_delete(this, *pPool); + *pPool = VMA_NULL; + return res; + } + + // Add to m_Pools. + { + VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex); + (*pPool)->SetId(m_NextPoolId++); + m_Pools.PushBack(*pPool); + } + + return VK_SUCCESS; +} + +void VmaAllocator_T::DestroyPool(VmaPool pool) +{ + // Remove from m_Pools. + { + VmaMutexLockWrite lock(m_PoolsMutex, m_UseMutex); + m_Pools.Remove(pool); + } + + vma_delete(this, pool); +} + +void VmaAllocator_T::GetPoolStatistics(VmaPool pool, VmaStatistics* pPoolStats) +{ + VmaClearStatistics(*pPoolStats); + pool->m_BlockVector.AddStatistics(*pPoolStats); + pool->m_DedicatedAllocations.AddStatistics(*pPoolStats); +} + +void VmaAllocator_T::CalculatePoolStatistics(VmaPool pool, VmaDetailedStatistics* pPoolStats) +{ + VmaClearDetailedStatistics(*pPoolStats); + pool->m_BlockVector.AddDetailedStatistics(*pPoolStats); + pool->m_DedicatedAllocations.AddDetailedStatistics(*pPoolStats); +} + +void VmaAllocator_T::SetCurrentFrameIndex(uint32_t frameIndex) +{ + m_CurrentFrameIndex.store(frameIndex); + +#if VMA_MEMORY_BUDGET + if(m_UseExtMemoryBudget) + { + UpdateVulkanBudget(); + } +#endif // #if VMA_MEMORY_BUDGET +} + +VkResult VmaAllocator_T::CheckPoolCorruption(VmaPool hPool) +{ + return hPool->m_BlockVector.CheckCorruption(); +} + +VkResult VmaAllocator_T::CheckCorruption(uint32_t memoryTypeBits) +{ + VkResult finalRes = VK_ERROR_FEATURE_NOT_PRESENT; + + // Process default pools. + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + VmaBlockVector* const pBlockVector = m_pBlockVectors[memTypeIndex]; + if(pBlockVector != VMA_NULL) + { + VkResult localRes = pBlockVector->CheckCorruption(); + switch(localRes) + { + case VK_ERROR_FEATURE_NOT_PRESENT: + break; + case VK_SUCCESS: + finalRes = VK_SUCCESS; + break; + default: + return localRes; + } + } + } + + // Process custom pools. + { + VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex); + for(VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool)) + { + if(((1u << pool->m_BlockVector.GetMemoryTypeIndex()) & memoryTypeBits) != 0) + { + VkResult localRes = pool->m_BlockVector.CheckCorruption(); + switch(localRes) + { + case VK_ERROR_FEATURE_NOT_PRESENT: + break; + case VK_SUCCESS: + finalRes = VK_SUCCESS; + break; + default: + return localRes; + } + } + } + } + + return finalRes; +} + +VkResult VmaAllocator_T::AllocateVulkanMemory(const VkMemoryAllocateInfo* pAllocateInfo, VkDeviceMemory* pMemory) +{ + AtomicTransactionalIncrement deviceMemoryCountIncrement; + const uint64_t prevDeviceMemoryCount = deviceMemoryCountIncrement.Increment(&m_DeviceMemoryCount); +#if VMA_DEBUG_DONT_EXCEED_MAX_MEMORY_ALLOCATION_COUNT + if(prevDeviceMemoryCount >= m_PhysicalDeviceProperties.limits.maxMemoryAllocationCount) + { + return VK_ERROR_TOO_MANY_OBJECTS; + } +#endif + + const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(pAllocateInfo->memoryTypeIndex); + + // HeapSizeLimit is in effect for this heap. + if((m_HeapSizeLimitMask & (1u << heapIndex)) != 0) + { + const VkDeviceSize heapSize = m_MemProps.memoryHeaps[heapIndex].size; + VkDeviceSize blockBytes = m_Budget.m_BlockBytes[heapIndex]; + for(;;) + { + const VkDeviceSize blockBytesAfterAllocation = blockBytes + pAllocateInfo->allocationSize; + if(blockBytesAfterAllocation > heapSize) + { + return VK_ERROR_OUT_OF_DEVICE_MEMORY; + } + if(m_Budget.m_BlockBytes[heapIndex].compare_exchange_strong(blockBytes, blockBytesAfterAllocation)) + { + break; + } + } + } + else + { + m_Budget.m_BlockBytes[heapIndex] += pAllocateInfo->allocationSize; + } + ++m_Budget.m_BlockCount[heapIndex]; + + // VULKAN CALL vkAllocateMemory. + VkResult res = (*m_VulkanFunctions.vkAllocateMemory)(m_hDevice, pAllocateInfo, GetAllocationCallbacks(), pMemory); + + if(res == VK_SUCCESS) + { +#if VMA_MEMORY_BUDGET + ++m_Budget.m_OperationsSinceBudgetFetch; +#endif + + // Informative callback. + if(m_DeviceMemoryCallbacks.pfnAllocate != VMA_NULL) + { + (*m_DeviceMemoryCallbacks.pfnAllocate)(this, pAllocateInfo->memoryTypeIndex, *pMemory, pAllocateInfo->allocationSize, m_DeviceMemoryCallbacks.pUserData); + } + + deviceMemoryCountIncrement.Commit(); + } + else + { + --m_Budget.m_BlockCount[heapIndex]; + m_Budget.m_BlockBytes[heapIndex] -= pAllocateInfo->allocationSize; + } + + return res; +} + +void VmaAllocator_T::FreeVulkanMemory(uint32_t memoryType, VkDeviceSize size, VkDeviceMemory hMemory) +{ + // Informative callback. + if(m_DeviceMemoryCallbacks.pfnFree != VMA_NULL) + { + (*m_DeviceMemoryCallbacks.pfnFree)(this, memoryType, hMemory, size, m_DeviceMemoryCallbacks.pUserData); + } + + // VULKAN CALL vkFreeMemory. + (*m_VulkanFunctions.vkFreeMemory)(m_hDevice, hMemory, GetAllocationCallbacks()); + + const uint32_t heapIndex = MemoryTypeIndexToHeapIndex(memoryType); + --m_Budget.m_BlockCount[heapIndex]; + m_Budget.m_BlockBytes[heapIndex] -= size; + + --m_DeviceMemoryCount; +} + +VkResult VmaAllocator_T::BindVulkanBuffer( + VkDeviceMemory memory, + VkDeviceSize memoryOffset, + VkBuffer buffer, + const void* pNext) +{ + if(pNext != VMA_NULL) + { +#if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2 + if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) && + m_VulkanFunctions.vkBindBufferMemory2KHR != VMA_NULL) + { + VkBindBufferMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_BUFFER_MEMORY_INFO_KHR }; + bindBufferMemoryInfo.pNext = pNext; + bindBufferMemoryInfo.buffer = buffer; + bindBufferMemoryInfo.memory = memory; + bindBufferMemoryInfo.memoryOffset = memoryOffset; + return (*m_VulkanFunctions.vkBindBufferMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo); + } + else +#endif // #if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2 + { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + } + else + { + return (*m_VulkanFunctions.vkBindBufferMemory)(m_hDevice, buffer, memory, memoryOffset); + } +} + +VkResult VmaAllocator_T::BindVulkanImage( + VkDeviceMemory memory, + VkDeviceSize memoryOffset, + VkImage image, + const void* pNext) +{ + if(pNext != VMA_NULL) + { +#if VMA_VULKAN_VERSION >= 1001000 || VMA_BIND_MEMORY2 + if((m_UseKhrBindMemory2 || m_VulkanApiVersion >= VK_MAKE_VERSION(1, 1, 0)) && + m_VulkanFunctions.vkBindImageMemory2KHR != VMA_NULL) + { + VkBindImageMemoryInfoKHR bindBufferMemoryInfo = { VK_STRUCTURE_TYPE_BIND_IMAGE_MEMORY_INFO_KHR }; + bindBufferMemoryInfo.pNext = pNext; + bindBufferMemoryInfo.image = image; + bindBufferMemoryInfo.memory = memory; + bindBufferMemoryInfo.memoryOffset = memoryOffset; + return (*m_VulkanFunctions.vkBindImageMemory2KHR)(m_hDevice, 1, &bindBufferMemoryInfo); + } + else +#endif // #if VMA_BIND_MEMORY2 + { + return VK_ERROR_EXTENSION_NOT_PRESENT; + } + } + else + { + return (*m_VulkanFunctions.vkBindImageMemory)(m_hDevice, image, memory, memoryOffset); + } +} + +VkResult VmaAllocator_T::Map(VmaAllocation hAllocation, void** ppData) +{ + switch(hAllocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock(); + char *pBytes = VMA_NULL; + VkResult res = pBlock->Map(this, 1, (void**)&pBytes); + if(res == VK_SUCCESS) + { + *ppData = pBytes + (ptrdiff_t)hAllocation->GetOffset(); + hAllocation->BlockAllocMap(); + } + return res; + } + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + return hAllocation->DedicatedAllocMap(this, ppData); + default: + VMA_ASSERT(0); + return VK_ERROR_MEMORY_MAP_FAILED; + } +} + +void VmaAllocator_T::Unmap(VmaAllocation hAllocation) +{ + switch(hAllocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock(); + hAllocation->BlockAllocUnmap(); + pBlock->Unmap(this, 1); + } + break; + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + hAllocation->DedicatedAllocUnmap(this); + break; + default: + VMA_ASSERT(0); + } +} + +VkResult VmaAllocator_T::BindBufferMemory( + VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkBuffer hBuffer, + const void* pNext) +{ + VkResult res = VK_ERROR_UNKNOWN; + switch(hAllocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + res = BindVulkanBuffer(hAllocation->GetMemory(), allocationLocalOffset, hBuffer, pNext); + break; + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + VmaDeviceMemoryBlock* const pBlock = hAllocation->GetBlock(); + VMA_ASSERT(pBlock && "Binding buffer to allocation that doesn't belong to any block."); + res = pBlock->BindBufferMemory(this, hAllocation, allocationLocalOffset, hBuffer, pNext); + break; + } + default: + VMA_ASSERT(0); + } + return res; +} + +VkResult VmaAllocator_T::BindImageMemory( + VmaAllocation hAllocation, + VkDeviceSize allocationLocalOffset, + VkImage hImage, + const void* pNext) +{ + VkResult res = VK_ERROR_UNKNOWN; + switch(hAllocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + res = BindVulkanImage(hAllocation->GetMemory(), allocationLocalOffset, hImage, pNext); + break; + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + VmaDeviceMemoryBlock* pBlock = hAllocation->GetBlock(); + VMA_ASSERT(pBlock && "Binding image to allocation that doesn't belong to any block."); + res = pBlock->BindImageMemory(this, hAllocation, allocationLocalOffset, hImage, pNext); + break; + } + default: + VMA_ASSERT(0); + } + return res; +} + +VkResult VmaAllocator_T::FlushOrInvalidateAllocation( + VmaAllocation hAllocation, + VkDeviceSize offset, VkDeviceSize size, + VMA_CACHE_OPERATION op) +{ + VkResult res = VK_SUCCESS; + + VkMappedMemoryRange memRange = {}; + if(GetFlushOrInvalidateRange(hAllocation, offset, size, memRange)) + { + switch(op) + { + case VMA_CACHE_FLUSH: + res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, 1, &memRange); + break; + case VMA_CACHE_INVALIDATE: + res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, 1, &memRange); + break; + default: + VMA_ASSERT(0); + } + } + // else: Just ignore this call. + return res; +} + +VkResult VmaAllocator_T::FlushOrInvalidateAllocations( + uint32_t allocationCount, + const VmaAllocation* allocations, + const VkDeviceSize* offsets, const VkDeviceSize* sizes, + VMA_CACHE_OPERATION op) +{ + typedef VmaStlAllocator RangeAllocator; + typedef VmaSmallVector RangeVector; + RangeVector ranges = RangeVector(RangeAllocator(GetAllocationCallbacks())); + + for(uint32_t allocIndex = 0; allocIndex < allocationCount; ++allocIndex) + { + const VmaAllocation alloc = allocations[allocIndex]; + const VkDeviceSize offset = offsets != VMA_NULL ? offsets[allocIndex] : 0; + const VkDeviceSize size = sizes != VMA_NULL ? sizes[allocIndex] : VK_WHOLE_SIZE; + VkMappedMemoryRange newRange; + if(GetFlushOrInvalidateRange(alloc, offset, size, newRange)) + { + ranges.push_back(newRange); + } + } + + VkResult res = VK_SUCCESS; + if(!ranges.empty()) + { + switch(op) + { + case VMA_CACHE_FLUSH: + res = (*GetVulkanFunctions().vkFlushMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data()); + break; + case VMA_CACHE_INVALIDATE: + res = (*GetVulkanFunctions().vkInvalidateMappedMemoryRanges)(m_hDevice, (uint32_t)ranges.size(), ranges.data()); + break; + default: + VMA_ASSERT(0); + } + } + // else: Just ignore this call. + return res; +} + +void VmaAllocator_T::FreeDedicatedMemory(const VmaAllocation allocation) +{ + VMA_ASSERT(allocation && allocation->GetType() == VmaAllocation_T::ALLOCATION_TYPE_DEDICATED); + + const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); + VmaPool parentPool = allocation->GetParentPool(); + if(parentPool == VK_NULL_HANDLE) + { + // Default pool + m_DedicatedAllocations[memTypeIndex].Unregister(allocation); + } + else + { + // Custom pool + parentPool->m_DedicatedAllocations.Unregister(allocation); + } + + VkDeviceMemory hMemory = allocation->GetMemory(); + + /* + There is no need to call this, because Vulkan spec allows to skip vkUnmapMemory + before vkFreeMemory. + + if(allocation->GetMappedData() != VMA_NULL) + { + (*m_VulkanFunctions.vkUnmapMemory)(m_hDevice, hMemory); + } + */ + + FreeVulkanMemory(memTypeIndex, allocation->GetSize(), hMemory); + + m_Budget.RemoveAllocation(MemoryTypeIndexToHeapIndex(allocation->GetMemoryTypeIndex()), allocation->GetSize()); + m_AllocationObjectAllocator.Free(allocation); + + VMA_DEBUG_LOG_FORMAT(" Freed DedicatedMemory MemoryTypeIndex=%u", memTypeIndex); +} + +uint32_t VmaAllocator_T::CalculateGpuDefragmentationMemoryTypeBits() const +{ + VkBufferCreateInfo dummyBufCreateInfo; + VmaFillGpuDefragmentationBufferCreateInfo(dummyBufCreateInfo); + + uint32_t memoryTypeBits = 0; + + // Create buffer. + VkBuffer buf = VK_NULL_HANDLE; + VkResult res = (*GetVulkanFunctions().vkCreateBuffer)( + m_hDevice, &dummyBufCreateInfo, GetAllocationCallbacks(), &buf); + if(res == VK_SUCCESS) + { + // Query for supported memory types. + VkMemoryRequirements memReq; + (*GetVulkanFunctions().vkGetBufferMemoryRequirements)(m_hDevice, buf, &memReq); + memoryTypeBits = memReq.memoryTypeBits; + + // Destroy buffer. + (*GetVulkanFunctions().vkDestroyBuffer)(m_hDevice, buf, GetAllocationCallbacks()); + } + + return memoryTypeBits; +} + +uint32_t VmaAllocator_T::CalculateGlobalMemoryTypeBits() const +{ + // Make sure memory information is already fetched. + VMA_ASSERT(GetMemoryTypeCount() > 0); + + uint32_t memoryTypeBits = UINT32_MAX; + + if(!m_UseAmdDeviceCoherentMemory) + { + // Exclude memory types that have VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD. + for(uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + if((m_MemProps.memoryTypes[memTypeIndex].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) != 0) + { + memoryTypeBits &= ~(1u << memTypeIndex); + } + } + } + + return memoryTypeBits; +} + +bool VmaAllocator_T::GetFlushOrInvalidateRange( + VmaAllocation allocation, + VkDeviceSize offset, VkDeviceSize size, + VkMappedMemoryRange& outRange) const +{ + const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); + if(size > 0 && IsMemoryTypeNonCoherent(memTypeIndex)) + { + const VkDeviceSize nonCoherentAtomSize = m_PhysicalDeviceProperties.limits.nonCoherentAtomSize; + const VkDeviceSize allocationSize = allocation->GetSize(); + VMA_ASSERT(offset <= allocationSize); + + outRange.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE; + outRange.pNext = VMA_NULL; + outRange.memory = allocation->GetMemory(); + + switch(allocation->GetType()) + { + case VmaAllocation_T::ALLOCATION_TYPE_DEDICATED: + outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize); + if(size == VK_WHOLE_SIZE) + { + outRange.size = allocationSize - outRange.offset; + } + else + { + VMA_ASSERT(offset + size <= allocationSize); + outRange.size = VMA_MIN( + VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize), + allocationSize - outRange.offset); + } + break; + case VmaAllocation_T::ALLOCATION_TYPE_BLOCK: + { + // 1. Still within this allocation. + outRange.offset = VmaAlignDown(offset, nonCoherentAtomSize); + if(size == VK_WHOLE_SIZE) + { + size = allocationSize - offset; + } + else + { + VMA_ASSERT(offset + size <= allocationSize); + } + outRange.size = VmaAlignUp(size + (offset - outRange.offset), nonCoherentAtomSize); + + // 2. Adjust to whole block. + const VkDeviceSize allocationOffset = allocation->GetOffset(); + VMA_ASSERT(allocationOffset % nonCoherentAtomSize == 0); + const VkDeviceSize blockSize = allocation->GetBlock()->m_pMetadata->GetSize(); + outRange.offset += allocationOffset; + outRange.size = VMA_MIN(outRange.size, blockSize - outRange.offset); + + break; + } + default: + VMA_ASSERT(0); + } + return true; + } + return false; +} + +#if VMA_MEMORY_BUDGET +void VmaAllocator_T::UpdateVulkanBudget() +{ + VMA_ASSERT(m_UseExtMemoryBudget); + + VkPhysicalDeviceMemoryProperties2KHR memProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2_KHR }; + + VkPhysicalDeviceMemoryBudgetPropertiesEXT budgetProps = { VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_BUDGET_PROPERTIES_EXT }; + VmaPnextChainPushFront(&memProps, &budgetProps); + + GetVulkanFunctions().vkGetPhysicalDeviceMemoryProperties2KHR(m_PhysicalDevice, &memProps); + + { + VmaMutexLockWrite lockWrite(m_Budget.m_BudgetMutex, m_UseMutex); + + for(uint32_t heapIndex = 0; heapIndex < GetMemoryHeapCount(); ++heapIndex) + { + m_Budget.m_VulkanUsage[heapIndex] = budgetProps.heapUsage[heapIndex]; + m_Budget.m_VulkanBudget[heapIndex] = budgetProps.heapBudget[heapIndex]; + m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] = m_Budget.m_BlockBytes[heapIndex].load(); + + // Some bugged drivers return the budget incorrectly, e.g. 0 or much bigger than heap size. + if(m_Budget.m_VulkanBudget[heapIndex] == 0) + { + m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size * 8 / 10; // 80% heuristics. + } + else if(m_Budget.m_VulkanBudget[heapIndex] > m_MemProps.memoryHeaps[heapIndex].size) + { + m_Budget.m_VulkanBudget[heapIndex] = m_MemProps.memoryHeaps[heapIndex].size; + } + if(m_Budget.m_VulkanUsage[heapIndex] == 0 && m_Budget.m_BlockBytesAtBudgetFetch[heapIndex] > 0) + { + m_Budget.m_VulkanUsage[heapIndex] = m_Budget.m_BlockBytesAtBudgetFetch[heapIndex]; + } + } + m_Budget.m_OperationsSinceBudgetFetch = 0; + } +} +#endif // VMA_MEMORY_BUDGET + +void VmaAllocator_T::FillAllocation(const VmaAllocation hAllocation, uint8_t pattern) +{ + if(VMA_DEBUG_INITIALIZE_ALLOCATIONS && + hAllocation->IsMappingAllowed() && + (m_MemProps.memoryTypes[hAllocation->GetMemoryTypeIndex()].propertyFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) != 0) + { + void* pData = VMA_NULL; + VkResult res = Map(hAllocation, &pData); + if(res == VK_SUCCESS) + { + memset(pData, (int)pattern, (size_t)hAllocation->GetSize()); + FlushOrInvalidateAllocation(hAllocation, 0, VK_WHOLE_SIZE, VMA_CACHE_FLUSH); + Unmap(hAllocation); + } + else + { + VMA_ASSERT(0 && "VMA_DEBUG_INITIALIZE_ALLOCATIONS is enabled, but couldn't map memory to fill allocation."); + } + } +} + +uint32_t VmaAllocator_T::GetGpuDefragmentationMemoryTypeBits() +{ + uint32_t memoryTypeBits = m_GpuDefragmentationMemoryTypeBits.load(); + if(memoryTypeBits == UINT32_MAX) + { + memoryTypeBits = CalculateGpuDefragmentationMemoryTypeBits(); + m_GpuDefragmentationMemoryTypeBits.store(memoryTypeBits); + } + return memoryTypeBits; +} + +#if VMA_STATS_STRING_ENABLED +void VmaAllocator_T::PrintDetailedMap(VmaJsonWriter& json) +{ + json.WriteString("DefaultPools"); + json.BeginObject(); + { + for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + VmaBlockVector* pBlockVector = m_pBlockVectors[memTypeIndex]; + VmaDedicatedAllocationList& dedicatedAllocList = m_DedicatedAllocations[memTypeIndex]; + if (pBlockVector != VMA_NULL) + { + json.BeginString("Type "); + json.ContinueString(memTypeIndex); + json.EndString(); + json.BeginObject(); + { + json.WriteString("PreferredBlockSize"); + json.WriteNumber(pBlockVector->GetPreferredBlockSize()); + + json.WriteString("Blocks"); + pBlockVector->PrintDetailedMap(json); + + json.WriteString("DedicatedAllocations"); + dedicatedAllocList.BuildStatsString(json); + } + json.EndObject(); + } + } + } + json.EndObject(); + + json.WriteString("CustomPools"); + json.BeginObject(); + { + VmaMutexLockRead lock(m_PoolsMutex, m_UseMutex); + if (!m_Pools.IsEmpty()) + { + for (uint32_t memTypeIndex = 0; memTypeIndex < GetMemoryTypeCount(); ++memTypeIndex) + { + bool displayType = true; + size_t index = 0; + for (VmaPool pool = m_Pools.Front(); pool != VMA_NULL; pool = m_Pools.GetNext(pool)) + { + VmaBlockVector& blockVector = pool->m_BlockVector; + if (blockVector.GetMemoryTypeIndex() == memTypeIndex) + { + if (displayType) + { + json.BeginString("Type "); + json.ContinueString(memTypeIndex); + json.EndString(); + json.BeginArray(); + displayType = false; + } + + json.BeginObject(); + { + json.WriteString("Name"); + json.BeginString(); + json.ContinueString((uint64_t)index++); + if (pool->GetName()) + { + json.ContinueString(" - "); + json.ContinueString(pool->GetName()); + } + json.EndString(); + + json.WriteString("PreferredBlockSize"); + json.WriteNumber(blockVector.GetPreferredBlockSize()); + + json.WriteString("Blocks"); + blockVector.PrintDetailedMap(json); + + json.WriteString("DedicatedAllocations"); + pool->m_DedicatedAllocations.BuildStatsString(json); + } + json.EndObject(); + } + } + + if (!displayType) + json.EndArray(); + } + } + } + json.EndObject(); +} +#endif // VMA_STATS_STRING_ENABLED +#endif // _VMA_ALLOCATOR_T_FUNCTIONS + + +#ifndef _VMA_PUBLIC_INTERFACE +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAllocator( + const VmaAllocatorCreateInfo* pCreateInfo, + VmaAllocator* pAllocator) +{ + VMA_ASSERT(pCreateInfo && pAllocator); + VMA_ASSERT(pCreateInfo->vulkanApiVersion == 0 || + (VK_VERSION_MAJOR(pCreateInfo->vulkanApiVersion) == 1 && VK_VERSION_MINOR(pCreateInfo->vulkanApiVersion) <= 3)); + VMA_DEBUG_LOG("vmaCreateAllocator"); + *pAllocator = vma_new(pCreateInfo->pAllocationCallbacks, VmaAllocator_T)(pCreateInfo); + VkResult result = (*pAllocator)->Init(pCreateInfo); + if(result < 0) + { + vma_delete(pCreateInfo->pAllocationCallbacks, *pAllocator); + *pAllocator = VK_NULL_HANDLE; + } + return result; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyAllocator( + VmaAllocator allocator) +{ + if(allocator != VK_NULL_HANDLE) + { + VMA_DEBUG_LOG("vmaDestroyAllocator"); + VkAllocationCallbacks allocationCallbacks = allocator->m_AllocationCallbacks; // Have to copy the callbacks when destroying. + vma_delete(&allocationCallbacks, allocator); + } +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocatorInfo(VmaAllocator allocator, VmaAllocatorInfo* pAllocatorInfo) +{ + VMA_ASSERT(allocator && pAllocatorInfo); + pAllocatorInfo->instance = allocator->m_hInstance; + pAllocatorInfo->physicalDevice = allocator->GetPhysicalDevice(); + pAllocatorInfo->device = allocator->m_hDevice; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetPhysicalDeviceProperties( + VmaAllocator allocator, + const VkPhysicalDeviceProperties **ppPhysicalDeviceProperties) +{ + VMA_ASSERT(allocator && ppPhysicalDeviceProperties); + *ppPhysicalDeviceProperties = &allocator->m_PhysicalDeviceProperties; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryProperties( + VmaAllocator allocator, + const VkPhysicalDeviceMemoryProperties** ppPhysicalDeviceMemoryProperties) +{ + VMA_ASSERT(allocator && ppPhysicalDeviceMemoryProperties); + *ppPhysicalDeviceMemoryProperties = &allocator->m_MemProps; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetMemoryTypeProperties( + VmaAllocator allocator, + uint32_t memoryTypeIndex, + VkMemoryPropertyFlags* pFlags) +{ + VMA_ASSERT(allocator && pFlags); + VMA_ASSERT(memoryTypeIndex < allocator->GetMemoryTypeCount()); + *pFlags = allocator->m_MemProps.memoryTypes[memoryTypeIndex].propertyFlags; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaSetCurrentFrameIndex( + VmaAllocator allocator, + uint32_t frameIndex) +{ + VMA_ASSERT(allocator); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->SetCurrentFrameIndex(frameIndex); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaCalculateStatistics( + VmaAllocator allocator, + VmaTotalStatistics* pStats) +{ + VMA_ASSERT(allocator && pStats); + VMA_DEBUG_GLOBAL_MUTEX_LOCK + allocator->CalculateStatistics(pStats); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetHeapBudgets( + VmaAllocator allocator, + VmaBudget* pBudgets) +{ + VMA_ASSERT(allocator && pBudgets); + VMA_DEBUG_GLOBAL_MUTEX_LOCK + allocator->GetHeapBudgets(pBudgets, 0, allocator->GetMemoryHeapCount()); +} + +#if VMA_STATS_STRING_ENABLED + +VMA_CALL_PRE void VMA_CALL_POST vmaBuildStatsString( + VmaAllocator allocator, + char** ppStatsString, + VkBool32 detailedMap) +{ + VMA_ASSERT(allocator && ppStatsString); + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VmaStringBuilder sb(allocator->GetAllocationCallbacks()); + { + VmaBudget budgets[VK_MAX_MEMORY_HEAPS]; + allocator->GetHeapBudgets(budgets, 0, allocator->GetMemoryHeapCount()); + + VmaTotalStatistics stats; + allocator->CalculateStatistics(&stats); + + VmaJsonWriter json(allocator->GetAllocationCallbacks(), sb); + json.BeginObject(); + { + json.WriteString("General"); + json.BeginObject(); + { + const VkPhysicalDeviceProperties& deviceProperties = allocator->m_PhysicalDeviceProperties; + const VkPhysicalDeviceMemoryProperties& memoryProperties = allocator->m_MemProps; + + json.WriteString("API"); + json.WriteString("Vulkan"); + + json.WriteString("apiVersion"); + json.BeginString(); + json.ContinueString(VK_VERSION_MAJOR(deviceProperties.apiVersion)); + json.ContinueString("."); + json.ContinueString(VK_VERSION_MINOR(deviceProperties.apiVersion)); + json.ContinueString("."); + json.ContinueString(VK_VERSION_PATCH(deviceProperties.apiVersion)); + json.EndString(); + + json.WriteString("GPU"); + json.WriteString(deviceProperties.deviceName); + json.WriteString("deviceType"); + json.WriteNumber(static_cast(deviceProperties.deviceType)); + + json.WriteString("maxMemoryAllocationCount"); + json.WriteNumber(deviceProperties.limits.maxMemoryAllocationCount); + json.WriteString("bufferImageGranularity"); + json.WriteNumber(deviceProperties.limits.bufferImageGranularity); + json.WriteString("nonCoherentAtomSize"); + json.WriteNumber(deviceProperties.limits.nonCoherentAtomSize); + + json.WriteString("memoryHeapCount"); + json.WriteNumber(memoryProperties.memoryHeapCount); + json.WriteString("memoryTypeCount"); + json.WriteNumber(memoryProperties.memoryTypeCount); + } + json.EndObject(); + } + { + json.WriteString("Total"); + VmaPrintDetailedStatistics(json, stats.total); + } + { + json.WriteString("MemoryInfo"); + json.BeginObject(); + { + for (uint32_t heapIndex = 0; heapIndex < allocator->GetMemoryHeapCount(); ++heapIndex) + { + json.BeginString("Heap "); + json.ContinueString(heapIndex); + json.EndString(); + json.BeginObject(); + { + const VkMemoryHeap& heapInfo = allocator->m_MemProps.memoryHeaps[heapIndex]; + json.WriteString("Flags"); + json.BeginArray(true); + { + if (heapInfo.flags & VK_MEMORY_HEAP_DEVICE_LOCAL_BIT) + json.WriteString("DEVICE_LOCAL"); + #if VMA_VULKAN_VERSION >= 1001000 + if (heapInfo.flags & VK_MEMORY_HEAP_MULTI_INSTANCE_BIT) + json.WriteString("MULTI_INSTANCE"); + #endif + + VkMemoryHeapFlags flags = heapInfo.flags & + ~(VK_MEMORY_HEAP_DEVICE_LOCAL_BIT + #if VMA_VULKAN_VERSION >= 1001000 + | VK_MEMORY_HEAP_MULTI_INSTANCE_BIT + #endif + ); + if (flags != 0) + json.WriteNumber(flags); + } + json.EndArray(); + + json.WriteString("Size"); + json.WriteNumber(heapInfo.size); + + json.WriteString("Budget"); + json.BeginObject(); + { + json.WriteString("BudgetBytes"); + json.WriteNumber(budgets[heapIndex].budget); + json.WriteString("UsageBytes"); + json.WriteNumber(budgets[heapIndex].usage); + } + json.EndObject(); + + json.WriteString("Stats"); + VmaPrintDetailedStatistics(json, stats.memoryHeap[heapIndex]); + + json.WriteString("MemoryPools"); + json.BeginObject(); + { + for (uint32_t typeIndex = 0; typeIndex < allocator->GetMemoryTypeCount(); ++typeIndex) + { + if (allocator->MemoryTypeIndexToHeapIndex(typeIndex) == heapIndex) + { + json.BeginString("Type "); + json.ContinueString(typeIndex); + json.EndString(); + json.BeginObject(); + { + json.WriteString("Flags"); + json.BeginArray(true); + { + VkMemoryPropertyFlags flags = allocator->m_MemProps.memoryTypes[typeIndex].propertyFlags; + if (flags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) + json.WriteString("DEVICE_LOCAL"); + if (flags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) + json.WriteString("HOST_VISIBLE"); + if (flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) + json.WriteString("HOST_COHERENT"); + if (flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) + json.WriteString("HOST_CACHED"); + if (flags & VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT) + json.WriteString("LAZILY_ALLOCATED"); + #if VMA_VULKAN_VERSION >= 1001000 + if (flags & VK_MEMORY_PROPERTY_PROTECTED_BIT) + json.WriteString("PROTECTED"); + #endif + #if VK_AMD_device_coherent_memory + if (flags & VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY) + json.WriteString("DEVICE_COHERENT_AMD"); + if (flags & VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY) + json.WriteString("DEVICE_UNCACHED_AMD"); + #endif + + flags &= ~(VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT + #if VMA_VULKAN_VERSION >= 1001000 + | VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT + #endif + #if VK_AMD_device_coherent_memory + | VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD_COPY + | VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD_COPY + #endif + | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT + | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT + | VK_MEMORY_PROPERTY_HOST_CACHED_BIT); + if (flags != 0) + json.WriteNumber(flags); + } + json.EndArray(); + + json.WriteString("Stats"); + VmaPrintDetailedStatistics(json, stats.memoryType[typeIndex]); + } + json.EndObject(); + } + } + + } + json.EndObject(); + } + json.EndObject(); + } + } + json.EndObject(); + } + + if (detailedMap == VK_TRUE) + allocator->PrintDetailedMap(json); + + json.EndObject(); + } + + *ppStatsString = VmaCreateStringCopy(allocator->GetAllocationCallbacks(), sb.GetData(), sb.GetLength()); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaFreeStatsString( + VmaAllocator allocator, + char* pStatsString) +{ + if(pStatsString != VMA_NULL) + { + VMA_ASSERT(allocator); + VmaFreeString(allocator->GetAllocationCallbacks(), pStatsString); + } +} + +#endif // VMA_STATS_STRING_ENABLED + +/* +This function is not protected by any mutex because it just reads immutable data. +*/ +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndex( + VmaAllocator allocator, + uint32_t memoryTypeBits, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + uint32_t* pMemoryTypeIndex) +{ + VMA_ASSERT(allocator != VK_NULL_HANDLE); + VMA_ASSERT(pAllocationCreateInfo != VMA_NULL); + VMA_ASSERT(pMemoryTypeIndex != VMA_NULL); + + return allocator->FindMemoryTypeIndex(memoryTypeBits, pAllocationCreateInfo, UINT32_MAX, pMemoryTypeIndex); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForBufferInfo( + VmaAllocator allocator, + const VkBufferCreateInfo* pBufferCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + uint32_t* pMemoryTypeIndex) +{ + VMA_ASSERT(allocator != VK_NULL_HANDLE); + VMA_ASSERT(pBufferCreateInfo != VMA_NULL); + VMA_ASSERT(pAllocationCreateInfo != VMA_NULL); + VMA_ASSERT(pMemoryTypeIndex != VMA_NULL); + + const VkDevice hDev = allocator->m_hDevice; + const VmaVulkanFunctions* funcs = &allocator->GetVulkanFunctions(); + VkResult res; + +#if VMA_VULKAN_VERSION >= 1003000 + if(funcs->vkGetDeviceBufferMemoryRequirements) + { + // Can query straight from VkBufferCreateInfo :) + VkDeviceBufferMemoryRequirements devBufMemReq = {VK_STRUCTURE_TYPE_DEVICE_BUFFER_MEMORY_REQUIREMENTS}; + devBufMemReq.pCreateInfo = pBufferCreateInfo; + + VkMemoryRequirements2 memReq = {VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2}; + (*funcs->vkGetDeviceBufferMemoryRequirements)(hDev, &devBufMemReq, &memReq); + + res = allocator->FindMemoryTypeIndex( + memReq.memoryRequirements.memoryTypeBits, pAllocationCreateInfo, pBufferCreateInfo->usage, pMemoryTypeIndex); + } + else +#endif // #if VMA_VULKAN_VERSION >= 1003000 + { + // Must create a dummy buffer to query :( + VkBuffer hBuffer = VK_NULL_HANDLE; + res = funcs->vkCreateBuffer( + hDev, pBufferCreateInfo, allocator->GetAllocationCallbacks(), &hBuffer); + if(res == VK_SUCCESS) + { + VkMemoryRequirements memReq = {}; + funcs->vkGetBufferMemoryRequirements(hDev, hBuffer, &memReq); + + res = allocator->FindMemoryTypeIndex( + memReq.memoryTypeBits, pAllocationCreateInfo, pBufferCreateInfo->usage, pMemoryTypeIndex); + + funcs->vkDestroyBuffer( + hDev, hBuffer, allocator->GetAllocationCallbacks()); + } + } + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFindMemoryTypeIndexForImageInfo( + VmaAllocator allocator, + const VkImageCreateInfo* pImageCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + uint32_t* pMemoryTypeIndex) +{ + VMA_ASSERT(allocator != VK_NULL_HANDLE); + VMA_ASSERT(pImageCreateInfo != VMA_NULL); + VMA_ASSERT(pAllocationCreateInfo != VMA_NULL); + VMA_ASSERT(pMemoryTypeIndex != VMA_NULL); + + const VkDevice hDev = allocator->m_hDevice; + const VmaVulkanFunctions* funcs = &allocator->GetVulkanFunctions(); + VkResult res; + +#if VMA_VULKAN_VERSION >= 1003000 + if(funcs->vkGetDeviceImageMemoryRequirements) + { + // Can query straight from VkImageCreateInfo :) + VkDeviceImageMemoryRequirements devImgMemReq = {VK_STRUCTURE_TYPE_DEVICE_IMAGE_MEMORY_REQUIREMENTS}; + devImgMemReq.pCreateInfo = pImageCreateInfo; + VMA_ASSERT(pImageCreateInfo->tiling != VK_IMAGE_TILING_DRM_FORMAT_MODIFIER_EXT_COPY && (pImageCreateInfo->flags & VK_IMAGE_CREATE_DISJOINT_BIT_COPY) == 0 && + "Cannot use this VkImageCreateInfo with vmaFindMemoryTypeIndexForImageInfo as I don't know what to pass as VkDeviceImageMemoryRequirements::planeAspect."); + + VkMemoryRequirements2 memReq = {VK_STRUCTURE_TYPE_MEMORY_REQUIREMENTS_2}; + (*funcs->vkGetDeviceImageMemoryRequirements)(hDev, &devImgMemReq, &memReq); + + res = allocator->FindMemoryTypeIndex( + memReq.memoryRequirements.memoryTypeBits, pAllocationCreateInfo, pImageCreateInfo->usage, pMemoryTypeIndex); + } + else +#endif // #if VMA_VULKAN_VERSION >= 1003000 + { + // Must create a dummy image to query :( + VkImage hImage = VK_NULL_HANDLE; + res = funcs->vkCreateImage( + hDev, pImageCreateInfo, allocator->GetAllocationCallbacks(), &hImage); + if(res == VK_SUCCESS) + { + VkMemoryRequirements memReq = {}; + funcs->vkGetImageMemoryRequirements(hDev, hImage, &memReq); + + res = allocator->FindMemoryTypeIndex( + memReq.memoryTypeBits, pAllocationCreateInfo, pImageCreateInfo->usage, pMemoryTypeIndex); + + funcs->vkDestroyImage( + hDev, hImage, allocator->GetAllocationCallbacks()); + } + } + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreatePool( + VmaAllocator allocator, + const VmaPoolCreateInfo* pCreateInfo, + VmaPool* pPool) +{ + VMA_ASSERT(allocator && pCreateInfo && pPool); + + VMA_DEBUG_LOG("vmaCreatePool"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->CreatePool(pCreateInfo, pPool); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyPool( + VmaAllocator allocator, + VmaPool pool) +{ + VMA_ASSERT(allocator); + + if(pool == VK_NULL_HANDLE) + { + return; + } + + VMA_DEBUG_LOG("vmaDestroyPool"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->DestroyPool(pool); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolStatistics( + VmaAllocator allocator, + VmaPool pool, + VmaStatistics* pPoolStats) +{ + VMA_ASSERT(allocator && pool && pPoolStats); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->GetPoolStatistics(pool, pPoolStats); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaCalculatePoolStatistics( + VmaAllocator allocator, + VmaPool pool, + VmaDetailedStatistics* pPoolStats) +{ + VMA_ASSERT(allocator && pool && pPoolStats); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->CalculatePoolStatistics(pool, pPoolStats); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckPoolCorruption(VmaAllocator allocator, VmaPool pool) +{ + VMA_ASSERT(allocator && pool); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VMA_DEBUG_LOG("vmaCheckPoolCorruption"); + + return allocator->CheckPoolCorruption(pool); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetPoolName( + VmaAllocator allocator, + VmaPool pool, + const char** ppName) +{ + VMA_ASSERT(allocator && pool && ppName); + + VMA_DEBUG_LOG("vmaGetPoolName"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + *ppName = pool->GetName(); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaSetPoolName( + VmaAllocator allocator, + VmaPool pool, + const char* pName) +{ + VMA_ASSERT(allocator && pool); + + VMA_DEBUG_LOG("vmaSetPoolName"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + pool->SetName(pName); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemory( + VmaAllocator allocator, + const VkMemoryRequirements* pVkMemoryRequirements, + const VmaAllocationCreateInfo* pCreateInfo, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocation); + + VMA_DEBUG_LOG("vmaAllocateMemory"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VkResult result = allocator->AllocateMemory( + *pVkMemoryRequirements, + false, // requiresDedicatedAllocation + false, // prefersDedicatedAllocation + VK_NULL_HANDLE, // dedicatedBuffer + VK_NULL_HANDLE, // dedicatedImage + UINT32_MAX, // dedicatedBufferImageUsage + *pCreateInfo, + VMA_SUBALLOCATION_TYPE_UNKNOWN, + 1, // allocationCount + pAllocation); + + if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } + + return result; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryPages( + VmaAllocator allocator, + const VkMemoryRequirements* pVkMemoryRequirements, + const VmaAllocationCreateInfo* pCreateInfo, + size_t allocationCount, + VmaAllocation* pAllocations, + VmaAllocationInfo* pAllocationInfo) +{ + if(allocationCount == 0) + { + return VK_SUCCESS; + } + + VMA_ASSERT(allocator && pVkMemoryRequirements && pCreateInfo && pAllocations); + + VMA_DEBUG_LOG("vmaAllocateMemoryPages"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VkResult result = allocator->AllocateMemory( + *pVkMemoryRequirements, + false, // requiresDedicatedAllocation + false, // prefersDedicatedAllocation + VK_NULL_HANDLE, // dedicatedBuffer + VK_NULL_HANDLE, // dedicatedImage + UINT32_MAX, // dedicatedBufferImageUsage + *pCreateInfo, + VMA_SUBALLOCATION_TYPE_UNKNOWN, + allocationCount, + pAllocations); + + if(pAllocationInfo != VMA_NULL && result == VK_SUCCESS) + { + for(size_t i = 0; i < allocationCount; ++i) + { + allocator->GetAllocationInfo(pAllocations[i], pAllocationInfo + i); + } + } + + return result; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForBuffer( + VmaAllocator allocator, + VkBuffer buffer, + const VmaAllocationCreateInfo* pCreateInfo, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && buffer != VK_NULL_HANDLE && pCreateInfo && pAllocation); + + VMA_DEBUG_LOG("vmaAllocateMemoryForBuffer"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VkMemoryRequirements vkMemReq = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + allocator->GetBufferMemoryRequirements(buffer, vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation); + + VkResult result = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + buffer, // dedicatedBuffer + VK_NULL_HANDLE, // dedicatedImage + UINT32_MAX, // dedicatedBufferImageUsage + *pCreateInfo, + VMA_SUBALLOCATION_TYPE_BUFFER, + 1, // allocationCount + pAllocation); + + if(pAllocationInfo && result == VK_SUCCESS) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } + + return result; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaAllocateMemoryForImage( + VmaAllocator allocator, + VkImage image, + const VmaAllocationCreateInfo* pCreateInfo, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && image != VK_NULL_HANDLE && pCreateInfo && pAllocation); + + VMA_DEBUG_LOG("vmaAllocateMemoryForImage"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + VkMemoryRequirements vkMemReq = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + allocator->GetImageMemoryRequirements(image, vkMemReq, + requiresDedicatedAllocation, prefersDedicatedAllocation); + + VkResult result = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + VK_NULL_HANDLE, // dedicatedBuffer + image, // dedicatedImage + UINT32_MAX, // dedicatedBufferImageUsage + *pCreateInfo, + VMA_SUBALLOCATION_TYPE_IMAGE_UNKNOWN, + 1, // allocationCount + pAllocation); + + if(pAllocationInfo && result == VK_SUCCESS) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } + + return result; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemory( + VmaAllocator allocator, + VmaAllocation allocation) +{ + VMA_ASSERT(allocator); + + if(allocation == VK_NULL_HANDLE) + { + return; + } + + VMA_DEBUG_LOG("vmaFreeMemory"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->FreeMemory( + 1, // allocationCount + &allocation); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaFreeMemoryPages( + VmaAllocator allocator, + size_t allocationCount, + const VmaAllocation* pAllocations) +{ + if(allocationCount == 0) + { + return; + } + + VMA_ASSERT(allocator); + + VMA_DEBUG_LOG("vmaFreeMemoryPages"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->FreeMemory(allocationCount, pAllocations); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationInfo( + VmaAllocator allocator, + VmaAllocation allocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && allocation && pAllocationInfo); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->GetAllocationInfo(allocation, pAllocationInfo); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationUserData( + VmaAllocator allocator, + VmaAllocation allocation, + void* pUserData) +{ + VMA_ASSERT(allocator && allocation); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocation->SetUserData(allocator, pUserData); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaSetAllocationName( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + const char* VMA_NULLABLE pName) +{ + allocation->SetName(allocator, pName); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetAllocationMemoryProperties( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkMemoryPropertyFlags* VMA_NOT_NULL pFlags) +{ + VMA_ASSERT(allocator && allocation && pFlags); + const uint32_t memTypeIndex = allocation->GetMemoryTypeIndex(); + *pFlags = allocator->m_MemProps.memoryTypes[memTypeIndex].propertyFlags; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaMapMemory( + VmaAllocator allocator, + VmaAllocation allocation, + void** ppData) +{ + VMA_ASSERT(allocator && allocation && ppData); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->Map(allocation, ppData); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaUnmapMemory( + VmaAllocator allocator, + VmaAllocation allocation) +{ + VMA_ASSERT(allocator && allocation); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + allocator->Unmap(allocation); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocation( + VmaAllocator allocator, + VmaAllocation allocation, + VkDeviceSize offset, + VkDeviceSize size) +{ + VMA_ASSERT(allocator && allocation); + + VMA_DEBUG_LOG("vmaFlushAllocation"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_FLUSH); + + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocation( + VmaAllocator allocator, + VmaAllocation allocation, + VkDeviceSize offset, + VkDeviceSize size) +{ + VMA_ASSERT(allocator && allocation); + + VMA_DEBUG_LOG("vmaInvalidateAllocation"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + const VkResult res = allocator->FlushOrInvalidateAllocation(allocation, offset, size, VMA_CACHE_INVALIDATE); + + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaFlushAllocations( + VmaAllocator allocator, + uint32_t allocationCount, + const VmaAllocation* allocations, + const VkDeviceSize* offsets, + const VkDeviceSize* sizes) +{ + VMA_ASSERT(allocator); + + if(allocationCount == 0) + { + return VK_SUCCESS; + } + + VMA_ASSERT(allocations); + + VMA_DEBUG_LOG("vmaFlushAllocations"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_FLUSH); + + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaInvalidateAllocations( + VmaAllocator allocator, + uint32_t allocationCount, + const VmaAllocation* allocations, + const VkDeviceSize* offsets, + const VkDeviceSize* sizes) +{ + VMA_ASSERT(allocator); + + if(allocationCount == 0) + { + return VK_SUCCESS; + } + + VMA_ASSERT(allocations); + + VMA_DEBUG_LOG("vmaInvalidateAllocations"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + const VkResult res = allocator->FlushOrInvalidateAllocations(allocationCount, allocations, offsets, sizes, VMA_CACHE_INVALIDATE); + + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCheckCorruption( + VmaAllocator allocator, + uint32_t memoryTypeBits) +{ + VMA_ASSERT(allocator); + + VMA_DEBUG_LOG("vmaCheckCorruption"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->CheckCorruption(memoryTypeBits); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentation( + VmaAllocator allocator, + const VmaDefragmentationInfo* pInfo, + VmaDefragmentationContext* pContext) +{ + VMA_ASSERT(allocator && pInfo && pContext); + + VMA_DEBUG_LOG("vmaBeginDefragmentation"); + + if (pInfo->pool != VMA_NULL) + { + // Check if run on supported algorithms + if (pInfo->pool->m_BlockVector.GetAlgorithm() & VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT) + return VK_ERROR_FEATURE_NOT_PRESENT; + } + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + *pContext = vma_new(allocator, VmaDefragmentationContext_T)(allocator, *pInfo); + return VK_SUCCESS; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaEndDefragmentation( + VmaAllocator allocator, + VmaDefragmentationContext context, + VmaDefragmentationStats* pStats) +{ + VMA_ASSERT(allocator && context); + + VMA_DEBUG_LOG("vmaEndDefragmentation"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + if (pStats) + context->GetStats(*pStats); + vma_delete(allocator, context); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBeginDefragmentationPass( + VmaAllocator VMA_NOT_NULL allocator, + VmaDefragmentationContext VMA_NOT_NULL context, + VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo) +{ + VMA_ASSERT(context && pPassInfo); + + VMA_DEBUG_LOG("vmaBeginDefragmentationPass"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return context->DefragmentPassBegin(*pPassInfo); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaEndDefragmentationPass( + VmaAllocator VMA_NOT_NULL allocator, + VmaDefragmentationContext VMA_NOT_NULL context, + VmaDefragmentationPassMoveInfo* VMA_NOT_NULL pPassInfo) +{ + VMA_ASSERT(context && pPassInfo); + + VMA_DEBUG_LOG("vmaEndDefragmentationPass"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return context->DefragmentPassEnd(*pPassInfo); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory( + VmaAllocator allocator, + VmaAllocation allocation, + VkBuffer buffer) +{ + VMA_ASSERT(allocator && allocation && buffer); + + VMA_DEBUG_LOG("vmaBindBufferMemory"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->BindBufferMemory(allocation, 0, buffer, VMA_NULL); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindBufferMemory2( + VmaAllocator allocator, + VmaAllocation allocation, + VkDeviceSize allocationLocalOffset, + VkBuffer buffer, + const void* pNext) +{ + VMA_ASSERT(allocator && allocation && buffer); + + VMA_DEBUG_LOG("vmaBindBufferMemory2"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->BindBufferMemory(allocation, allocationLocalOffset, buffer, pNext); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory( + VmaAllocator allocator, + VmaAllocation allocation, + VkImage image) +{ + VMA_ASSERT(allocator && allocation && image); + + VMA_DEBUG_LOG("vmaBindImageMemory"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->BindImageMemory(allocation, 0, image, VMA_NULL); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaBindImageMemory2( + VmaAllocator allocator, + VmaAllocation allocation, + VkDeviceSize allocationLocalOffset, + VkImage image, + const void* pNext) +{ + VMA_ASSERT(allocator && allocation && image); + + VMA_DEBUG_LOG("vmaBindImageMemory2"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + return allocator->BindImageMemory(allocation, allocationLocalOffset, image, pNext); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBuffer( + VmaAllocator allocator, + const VkBufferCreateInfo* pBufferCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + VkBuffer* pBuffer, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && pBuffer && pAllocation); + + if(pBufferCreateInfo->size == 0) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 && + !allocator->m_UseKhrBufferDeviceAddress) + { + VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used."); + return VK_ERROR_INITIALIZATION_FAILED; + } + + VMA_DEBUG_LOG("vmaCreateBuffer"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + *pBuffer = VK_NULL_HANDLE; + *pAllocation = VK_NULL_HANDLE; + + // 1. Create VkBuffer. + VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)( + allocator->m_hDevice, + pBufferCreateInfo, + allocator->GetAllocationCallbacks(), + pBuffer); + if(res >= 0) + { + // 2. vkGetBufferMemoryRequirements. + VkMemoryRequirements vkMemReq = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq, + requiresDedicatedAllocation, prefersDedicatedAllocation); + + // 3. Allocate memory using allocator. + res = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + *pBuffer, // dedicatedBuffer + VK_NULL_HANDLE, // dedicatedImage + pBufferCreateInfo->usage, // dedicatedBufferImageUsage + *pAllocationCreateInfo, + VMA_SUBALLOCATION_TYPE_BUFFER, + 1, // allocationCount + pAllocation); + + if(res >= 0) + { + // 3. Bind buffer with memory. + if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0) + { + res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL); + } + if(res >= 0) + { + // All steps succeeded. + #if VMA_STATS_STRING_ENABLED + (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage); + #endif + if(pAllocationInfo != VMA_NULL) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } + + return VK_SUCCESS; + } + allocator->FreeMemory( + 1, // allocationCount + pAllocation); + *pAllocation = VK_NULL_HANDLE; + (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks()); + *pBuffer = VK_NULL_HANDLE; + return res; + } + (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks()); + *pBuffer = VK_NULL_HANDLE; + return res; + } + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateBufferWithAlignment( + VmaAllocator allocator, + const VkBufferCreateInfo* pBufferCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + VkDeviceSize minAlignment, + VkBuffer* pBuffer, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && pBufferCreateInfo && pAllocationCreateInfo && VmaIsPow2(minAlignment) && pBuffer && pAllocation); + + if(pBufferCreateInfo->size == 0) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + if((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 && + !allocator->m_UseKhrBufferDeviceAddress) + { + VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used."); + return VK_ERROR_INITIALIZATION_FAILED; + } + + VMA_DEBUG_LOG("vmaCreateBufferWithAlignment"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + *pBuffer = VK_NULL_HANDLE; + *pAllocation = VK_NULL_HANDLE; + + // 1. Create VkBuffer. + VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)( + allocator->m_hDevice, + pBufferCreateInfo, + allocator->GetAllocationCallbacks(), + pBuffer); + if(res >= 0) + { + // 2. vkGetBufferMemoryRequirements. + VkMemoryRequirements vkMemReq = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + allocator->GetBufferMemoryRequirements(*pBuffer, vkMemReq, + requiresDedicatedAllocation, prefersDedicatedAllocation); + + // 2a. Include minAlignment + vkMemReq.alignment = VMA_MAX(vkMemReq.alignment, minAlignment); + + // 3. Allocate memory using allocator. + res = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + *pBuffer, // dedicatedBuffer + VK_NULL_HANDLE, // dedicatedImage + pBufferCreateInfo->usage, // dedicatedBufferImageUsage + *pAllocationCreateInfo, + VMA_SUBALLOCATION_TYPE_BUFFER, + 1, // allocationCount + pAllocation); + + if(res >= 0) + { + // 3. Bind buffer with memory. + if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0) + { + res = allocator->BindBufferMemory(*pAllocation, 0, *pBuffer, VMA_NULL); + } + if(res >= 0) + { + // All steps succeeded. + #if VMA_STATS_STRING_ENABLED + (*pAllocation)->InitBufferImageUsage(pBufferCreateInfo->usage); + #endif + if(pAllocationInfo != VMA_NULL) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } + + return VK_SUCCESS; + } + allocator->FreeMemory( + 1, // allocationCount + pAllocation); + *pAllocation = VK_NULL_HANDLE; + (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks()); + *pBuffer = VK_NULL_HANDLE; + return res; + } + (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks()); + *pBuffer = VK_NULL_HANDLE; + return res; + } + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo, + VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer) +{ + return vmaCreateAliasingBuffer2(allocator, allocation, 0, pBufferCreateInfo, pBuffer); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingBuffer2( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkDeviceSize allocationLocalOffset, + const VkBufferCreateInfo* VMA_NOT_NULL pBufferCreateInfo, + VkBuffer VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pBuffer) +{ + VMA_ASSERT(allocator && pBufferCreateInfo && pBuffer && allocation); + VMA_ASSERT(allocationLocalOffset + pBufferCreateInfo->size <= allocation->GetSize()); + + VMA_DEBUG_LOG("vmaCreateAliasingBuffer2"); + + *pBuffer = VK_NULL_HANDLE; + + if (pBufferCreateInfo->size == 0) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + if ((pBufferCreateInfo->usage & VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT_COPY) != 0 && + !allocator->m_UseKhrBufferDeviceAddress) + { + VMA_ASSERT(0 && "Creating a buffer with VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT is not valid if VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT was not used."); + return VK_ERROR_INITIALIZATION_FAILED; + } + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + // 1. Create VkBuffer. + VkResult res = (*allocator->GetVulkanFunctions().vkCreateBuffer)( + allocator->m_hDevice, + pBufferCreateInfo, + allocator->GetAllocationCallbacks(), + pBuffer); + if (res >= 0) + { + // 2. Bind buffer with memory. + res = allocator->BindBufferMemory(allocation, allocationLocalOffset, *pBuffer, VMA_NULL); + if (res >= 0) + { + return VK_SUCCESS; + } + (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, *pBuffer, allocator->GetAllocationCallbacks()); + } + return res; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyBuffer( + VmaAllocator allocator, + VkBuffer buffer, + VmaAllocation allocation) +{ + VMA_ASSERT(allocator); + + if(buffer == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE) + { + return; + } + + VMA_DEBUG_LOG("vmaDestroyBuffer"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + if(buffer != VK_NULL_HANDLE) + { + (*allocator->GetVulkanFunctions().vkDestroyBuffer)(allocator->m_hDevice, buffer, allocator->GetAllocationCallbacks()); + } + + if(allocation != VK_NULL_HANDLE) + { + allocator->FreeMemory( + 1, // allocationCount + &allocation); + } +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateImage( + VmaAllocator allocator, + const VkImageCreateInfo* pImageCreateInfo, + const VmaAllocationCreateInfo* pAllocationCreateInfo, + VkImage* pImage, + VmaAllocation* pAllocation, + VmaAllocationInfo* pAllocationInfo) +{ + VMA_ASSERT(allocator && pImageCreateInfo && pAllocationCreateInfo && pImage && pAllocation); + + if(pImageCreateInfo->extent.width == 0 || + pImageCreateInfo->extent.height == 0 || + pImageCreateInfo->extent.depth == 0 || + pImageCreateInfo->mipLevels == 0 || + pImageCreateInfo->arrayLayers == 0) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + + VMA_DEBUG_LOG("vmaCreateImage"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + *pImage = VK_NULL_HANDLE; + *pAllocation = VK_NULL_HANDLE; + + // 1. Create VkImage. + VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)( + allocator->m_hDevice, + pImageCreateInfo, + allocator->GetAllocationCallbacks(), + pImage); + if(res >= 0) + { + VmaSuballocationType suballocType = pImageCreateInfo->tiling == VK_IMAGE_TILING_OPTIMAL ? + VMA_SUBALLOCATION_TYPE_IMAGE_OPTIMAL : + VMA_SUBALLOCATION_TYPE_IMAGE_LINEAR; + + // 2. Allocate memory using allocator. + VkMemoryRequirements vkMemReq = {}; + bool requiresDedicatedAllocation = false; + bool prefersDedicatedAllocation = false; + allocator->GetImageMemoryRequirements(*pImage, vkMemReq, + requiresDedicatedAllocation, prefersDedicatedAllocation); + + res = allocator->AllocateMemory( + vkMemReq, + requiresDedicatedAllocation, + prefersDedicatedAllocation, + VK_NULL_HANDLE, // dedicatedBuffer + *pImage, // dedicatedImage + pImageCreateInfo->usage, // dedicatedBufferImageUsage + *pAllocationCreateInfo, + suballocType, + 1, // allocationCount + pAllocation); + + if(res >= 0) + { + // 3. Bind image with memory. + if((pAllocationCreateInfo->flags & VMA_ALLOCATION_CREATE_DONT_BIND_BIT) == 0) + { + res = allocator->BindImageMemory(*pAllocation, 0, *pImage, VMA_NULL); + } + if(res >= 0) + { + // All steps succeeded. + #if VMA_STATS_STRING_ENABLED + (*pAllocation)->InitBufferImageUsage(pImageCreateInfo->usage); + #endif + if(pAllocationInfo != VMA_NULL) + { + allocator->GetAllocationInfo(*pAllocation, pAllocationInfo); + } + + return VK_SUCCESS; + } + allocator->FreeMemory( + 1, // allocationCount + pAllocation); + *pAllocation = VK_NULL_HANDLE; + (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks()); + *pImage = VK_NULL_HANDLE; + return res; + } + (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks()); + *pImage = VK_NULL_HANDLE; + return res; + } + return res; +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo, + VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage) +{ + return vmaCreateAliasingImage2(allocator, allocation, 0, pImageCreateInfo, pImage); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateAliasingImage2( + VmaAllocator VMA_NOT_NULL allocator, + VmaAllocation VMA_NOT_NULL allocation, + VkDeviceSize allocationLocalOffset, + const VkImageCreateInfo* VMA_NOT_NULL pImageCreateInfo, + VkImage VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pImage) +{ + VMA_ASSERT(allocator && pImageCreateInfo && pImage && allocation); + + *pImage = VK_NULL_HANDLE; + + VMA_DEBUG_LOG("vmaCreateImage2"); + + if (pImageCreateInfo->extent.width == 0 || + pImageCreateInfo->extent.height == 0 || + pImageCreateInfo->extent.depth == 0 || + pImageCreateInfo->mipLevels == 0 || + pImageCreateInfo->arrayLayers == 0) + { + return VK_ERROR_INITIALIZATION_FAILED; + } + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + // 1. Create VkImage. + VkResult res = (*allocator->GetVulkanFunctions().vkCreateImage)( + allocator->m_hDevice, + pImageCreateInfo, + allocator->GetAllocationCallbacks(), + pImage); + if (res >= 0) + { + // 2. Bind image with memory. + res = allocator->BindImageMemory(allocation, allocationLocalOffset, *pImage, VMA_NULL); + if (res >= 0) + { + return VK_SUCCESS; + } + (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, *pImage, allocator->GetAllocationCallbacks()); + } + return res; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyImage( + VmaAllocator VMA_NOT_NULL allocator, + VkImage VMA_NULLABLE_NON_DISPATCHABLE image, + VmaAllocation VMA_NULLABLE allocation) +{ + VMA_ASSERT(allocator); + + if(image == VK_NULL_HANDLE && allocation == VK_NULL_HANDLE) + { + return; + } + + VMA_DEBUG_LOG("vmaDestroyImage"); + + VMA_DEBUG_GLOBAL_MUTEX_LOCK + + if(image != VK_NULL_HANDLE) + { + (*allocator->GetVulkanFunctions().vkDestroyImage)(allocator->m_hDevice, image, allocator->GetAllocationCallbacks()); + } + if(allocation != VK_NULL_HANDLE) + { + allocator->FreeMemory( + 1, // allocationCount + &allocation); + } +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaCreateVirtualBlock( + const VmaVirtualBlockCreateInfo* VMA_NOT_NULL pCreateInfo, + VmaVirtualBlock VMA_NULLABLE * VMA_NOT_NULL pVirtualBlock) +{ + VMA_ASSERT(pCreateInfo && pVirtualBlock); + VMA_ASSERT(pCreateInfo->size > 0); + VMA_DEBUG_LOG("vmaCreateVirtualBlock"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + *pVirtualBlock = vma_new(pCreateInfo->pAllocationCallbacks, VmaVirtualBlock_T)(*pCreateInfo); + VkResult res = (*pVirtualBlock)->Init(); + if(res < 0) + { + vma_delete(pCreateInfo->pAllocationCallbacks, *pVirtualBlock); + *pVirtualBlock = VK_NULL_HANDLE; + } + return res; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaDestroyVirtualBlock(VmaVirtualBlock VMA_NULLABLE virtualBlock) +{ + if(virtualBlock != VK_NULL_HANDLE) + { + VMA_DEBUG_LOG("vmaDestroyVirtualBlock"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + VkAllocationCallbacks allocationCallbacks = virtualBlock->m_AllocationCallbacks; // Have to copy the callbacks when destroying. + vma_delete(&allocationCallbacks, virtualBlock); + } +} + +VMA_CALL_PRE VkBool32 VMA_CALL_POST vmaIsVirtualBlockEmpty(VmaVirtualBlock VMA_NOT_NULL virtualBlock) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE); + VMA_DEBUG_LOG("vmaIsVirtualBlockEmpty"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + return virtualBlock->IsEmpty() ? VK_TRUE : VK_FALSE; +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualAllocationInfo(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, VmaVirtualAllocationInfo* VMA_NOT_NULL pVirtualAllocInfo) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pVirtualAllocInfo != VMA_NULL); + VMA_DEBUG_LOG("vmaGetVirtualAllocationInfo"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + virtualBlock->GetAllocationInfo(allocation, *pVirtualAllocInfo); +} + +VMA_CALL_PRE VkResult VMA_CALL_POST vmaVirtualAllocate(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + const VmaVirtualAllocationCreateInfo* VMA_NOT_NULL pCreateInfo, VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE* VMA_NOT_NULL pAllocation, + VkDeviceSize* VMA_NULLABLE pOffset) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pCreateInfo != VMA_NULL && pAllocation != VMA_NULL); + VMA_DEBUG_LOG("vmaVirtualAllocate"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + return virtualBlock->Allocate(*pCreateInfo, *pAllocation, pOffset); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaVirtualFree(VmaVirtualBlock VMA_NOT_NULL virtualBlock, VmaVirtualAllocation VMA_NULLABLE_NON_DISPATCHABLE allocation) +{ + if(allocation != VK_NULL_HANDLE) + { + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE); + VMA_DEBUG_LOG("vmaVirtualFree"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + virtualBlock->Free(allocation); + } +} + +VMA_CALL_PRE void VMA_CALL_POST vmaClearVirtualBlock(VmaVirtualBlock VMA_NOT_NULL virtualBlock) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE); + VMA_DEBUG_LOG("vmaClearVirtualBlock"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + virtualBlock->Clear(); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaSetVirtualAllocationUserData(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaVirtualAllocation VMA_NOT_NULL_NON_DISPATCHABLE allocation, void* VMA_NULLABLE pUserData) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE); + VMA_DEBUG_LOG("vmaSetVirtualAllocationUserData"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + virtualBlock->SetAllocationUserData(allocation, pUserData); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaGetVirtualBlockStatistics(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaStatistics* VMA_NOT_NULL pStats) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pStats != VMA_NULL); + VMA_DEBUG_LOG("vmaGetVirtualBlockStatistics"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + virtualBlock->GetStatistics(*pStats); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaCalculateVirtualBlockStatistics(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + VmaDetailedStatistics* VMA_NOT_NULL pStats) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && pStats != VMA_NULL); + VMA_DEBUG_LOG("vmaCalculateVirtualBlockStatistics"); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + virtualBlock->CalculateDetailedStatistics(*pStats); +} + +#if VMA_STATS_STRING_ENABLED + +VMA_CALL_PRE void VMA_CALL_POST vmaBuildVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + char* VMA_NULLABLE * VMA_NOT_NULL ppStatsString, VkBool32 detailedMap) +{ + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE && ppStatsString != VMA_NULL); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + const VkAllocationCallbacks* allocationCallbacks = virtualBlock->GetAllocationCallbacks(); + VmaStringBuilder sb(allocationCallbacks); + virtualBlock->BuildStatsString(detailedMap != VK_FALSE, sb); + *ppStatsString = VmaCreateStringCopy(allocationCallbacks, sb.GetData(), sb.GetLength()); +} + +VMA_CALL_PRE void VMA_CALL_POST vmaFreeVirtualBlockStatsString(VmaVirtualBlock VMA_NOT_NULL virtualBlock, + char* VMA_NULLABLE pStatsString) +{ + if(pStatsString != VMA_NULL) + { + VMA_ASSERT(virtualBlock != VK_NULL_HANDLE); + VMA_DEBUG_GLOBAL_MUTEX_LOCK; + VmaFreeString(virtualBlock->GetAllocationCallbacks(), pStatsString); + } +} +#endif // VMA_STATS_STRING_ENABLED +#endif // _VMA_PUBLIC_INTERFACE +#endif // VMA_IMPLEMENTATION + +/** +\page quick_start Quick start + +\section quick_start_project_setup Project setup + +Vulkan Memory Allocator comes in form of a "stb-style" single header file. +You don't need to build it as a separate library project. +You can add this file directly to your project and submit it to code repository next to your other source files. + +"Single header" doesn't mean that everything is contained in C/C++ declarations, +like it tends to be in case of inline functions or C++ templates. +It means that implementation is bundled with interface in a single file and needs to be extracted using preprocessor macro. +If you don't do it properly, you will get linker errors. + +To do it properly: + +-# Include "vk_mem_alloc.h" file in each CPP file where you want to use the library. + This includes declarations of all members of the library. +-# In exactly one CPP file define following macro before this include. + It enables also internal definitions. + +\code +#define VMA_IMPLEMENTATION +#include "vk_mem_alloc.h" +\endcode + +It may be a good idea to create dedicated CPP file just for this purpose. + +This library includes header ``, which in turn +includes `` on Windows. If you need some specific macros defined +before including these headers (like `WIN32_LEAN_AND_MEAN` or +`WINVER` for Windows, `VK_USE_PLATFORM_WIN32_KHR` for Vulkan), you must define +them before every `#include` of this library. + +This library is written in C++, but has C-compatible interface. +Thus you can include and use vk_mem_alloc.h in C or C++ code, but full +implementation with `VMA_IMPLEMENTATION` macro must be compiled as C++, NOT as C. +Some features of C++14 are used. STL containers, RTTI, or C++ exceptions are not used. + + +\section quick_start_initialization Initialization + +At program startup: + +-# Initialize Vulkan to have `VkPhysicalDevice`, `VkDevice` and `VkInstance` object. +-# Fill VmaAllocatorCreateInfo structure and create #VmaAllocator object by + calling vmaCreateAllocator(). + +Only members `physicalDevice`, `device`, `instance` are required. +However, you should inform the library which Vulkan version do you use by setting +VmaAllocatorCreateInfo::vulkanApiVersion and which extensions did you enable +by setting VmaAllocatorCreateInfo::flags (like #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT for VK_KHR_buffer_device_address). +Otherwise, VMA would use only features of Vulkan 1.0 core with no extensions. + +\subsection quick_start_initialization_selecting_vulkan_version Selecting Vulkan version + +VMA supports Vulkan version down to 1.0, for backward compatibility. +If you want to use higher version, you need to inform the library about it. +This is a two-step process. + +Step 1: Compile time. By default, VMA compiles with code supporting the highest +Vulkan version found in the included `` that is also supported by the library. +If this is OK, you don't need to do anything. +However, if you want to compile VMA as if only some lower Vulkan version was available, +define macro `VMA_VULKAN_VERSION` before every `#include "vk_mem_alloc.h"`. +It should have decimal numeric value in form of ABBBCCC, where A = major, BBB = minor, CCC = patch Vulkan version. +For example, to compile against Vulkan 1.2: + +\code +#define VMA_VULKAN_VERSION 1002000 // Vulkan 1.2 +#include "vk_mem_alloc.h" +\endcode + +Step 2: Runtime. Even when compiled with higher Vulkan version available, +VMA can use only features of a lower version, which is configurable during creation of the #VmaAllocator object. +By default, only Vulkan 1.0 is used. +To initialize the allocator with support for higher Vulkan version, you need to set member +VmaAllocatorCreateInfo::vulkanApiVersion to an appropriate value, e.g. using constants like `VK_API_VERSION_1_2`. +See code sample below. + +\subsection quick_start_initialization_importing_vulkan_functions Importing Vulkan functions + +You may need to configure importing Vulkan functions. There are 3 ways to do this: + +-# **If you link with Vulkan static library** (e.g. "vulkan-1.lib" on Windows): + - You don't need to do anything. + - VMA will use these, as macro `VMA_STATIC_VULKAN_FUNCTIONS` is defined to 1 by default. +-# **If you want VMA to fetch pointers to Vulkan functions dynamically** using `vkGetInstanceProcAddr`, + `vkGetDeviceProcAddr` (this is the option presented in the example below): + - Define `VMA_STATIC_VULKAN_FUNCTIONS` to 0, `VMA_DYNAMIC_VULKAN_FUNCTIONS` to 1. + - Provide pointers to these two functions via VmaVulkanFunctions::vkGetInstanceProcAddr, + VmaVulkanFunctions::vkGetDeviceProcAddr. + - The library will fetch pointers to all other functions it needs internally. +-# **If you fetch pointers to all Vulkan functions in a custom way**, e.g. using some loader like + [Volk](https://github.com/zeux/volk): + - Define `VMA_STATIC_VULKAN_FUNCTIONS` and `VMA_DYNAMIC_VULKAN_FUNCTIONS` to 0. + - Pass these pointers via structure #VmaVulkanFunctions. + +Example for case 2: + +\code +#define VMA_STATIC_VULKAN_FUNCTIONS 0 +#define VMA_DYNAMIC_VULKAN_FUNCTIONS 1 +#include "vk_mem_alloc.h" + +... + +VmaVulkanFunctions vulkanFunctions = {}; +vulkanFunctions.vkGetInstanceProcAddr = &vkGetInstanceProcAddr; +vulkanFunctions.vkGetDeviceProcAddr = &vkGetDeviceProcAddr; + +VmaAllocatorCreateInfo allocatorCreateInfo = {}; +allocatorCreateInfo.vulkanApiVersion = VK_API_VERSION_1_2; +allocatorCreateInfo.physicalDevice = physicalDevice; +allocatorCreateInfo.device = device; +allocatorCreateInfo.instance = instance; +allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions; + +VmaAllocator allocator; +vmaCreateAllocator(&allocatorCreateInfo, &allocator); +\endcode + + +\section quick_start_resource_allocation Resource allocation + +When you want to create a buffer or image: + +-# Fill `VkBufferCreateInfo` / `VkImageCreateInfo` structure. +-# Fill VmaAllocationCreateInfo structure. +-# Call vmaCreateBuffer() / vmaCreateImage() to get `VkBuffer`/`VkImage` with memory + already allocated and bound to it, plus #VmaAllocation objects that represents its underlying memory. + +\code +VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufferInfo.size = 65536; +bufferInfo.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocInfo = {}; +allocInfo.usage = VMA_MEMORY_USAGE_AUTO; + +VkBuffer buffer; +VmaAllocation allocation; +vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); +\endcode + +Don't forget to destroy your objects when no longer needed: + +\code +vmaDestroyBuffer(allocator, buffer, allocation); +vmaDestroyAllocator(allocator); +\endcode + + +\page choosing_memory_type Choosing memory type + +Physical devices in Vulkan support various combinations of memory heaps and +types. Help with choosing correct and optimal memory type for your specific +resource is one of the key features of this library. You can use it by filling +appropriate members of VmaAllocationCreateInfo structure, as described below. +You can also combine multiple methods. + +-# If you just want to find memory type index that meets your requirements, you + can use function: vmaFindMemoryTypeIndexForBufferInfo(), + vmaFindMemoryTypeIndexForImageInfo(), vmaFindMemoryTypeIndex(). +-# If you want to allocate a region of device memory without association with any + specific image or buffer, you can use function vmaAllocateMemory(). Usage of + this function is not recommended and usually not needed. + vmaAllocateMemoryPages() function is also provided for creating multiple allocations at once, + which may be useful for sparse binding. +-# If you already have a buffer or an image created, you want to allocate memory + for it and then you will bind it yourself, you can use function + vmaAllocateMemoryForBuffer(), vmaAllocateMemoryForImage(). + For binding you should use functions: vmaBindBufferMemory(), vmaBindImageMemory() + or their extended versions: vmaBindBufferMemory2(), vmaBindImageMemory2(). +-# **This is the easiest and recommended way to use this library:** + If you want to create a buffer or an image, allocate memory for it and bind + them together, all in one call, you can use function vmaCreateBuffer(), + vmaCreateImage(). + +When using 3. or 4., the library internally queries Vulkan for memory types +supported for that buffer or image (function `vkGetBufferMemoryRequirements()`) +and uses only one of these types. + +If no memory type can be found that meets all the requirements, these functions +return `VK_ERROR_FEATURE_NOT_PRESENT`. + +You can leave VmaAllocationCreateInfo structure completely filled with zeros. +It means no requirements are specified for memory type. +It is valid, although not very useful. + +\section choosing_memory_type_usage Usage + +The easiest way to specify memory requirements is to fill member +VmaAllocationCreateInfo::usage using one of the values of enum #VmaMemoryUsage. +It defines high level, common usage types. +Since version 3 of the library, it is recommended to use #VMA_MEMORY_USAGE_AUTO to let it select best memory type for your resource automatically. + +For example, if you want to create a uniform buffer that will be filled using +transfer only once or infrequently and then used for rendering every frame as a uniform buffer, you can +do it using following code. The buffer will most likely end up in a memory type with +`VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT` to be fast to access by the GPU device. + +\code +VkBufferCreateInfo bufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufferInfo.size = 65536; +bufferInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocInfo = {}; +allocInfo.usage = VMA_MEMORY_USAGE_AUTO; + +VkBuffer buffer; +VmaAllocation allocation; +vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); +\endcode + +If you have a preference for putting the resource in GPU (device) memory or CPU (host) memory +on systems with discrete graphics card that have the memories separate, you can use +#VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE or #VMA_MEMORY_USAGE_AUTO_PREFER_HOST. + +When using `VMA_MEMORY_USAGE_AUTO*` while you want to map the allocated memory, +you also need to specify one of the host access flags: +#VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT. +This will help the library decide about preferred memory type to ensure it has `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` +so you can map it. + +For example, a staging buffer that will be filled via mapped pointer and then +used as a source of transfer to the buffer described previously can be created like this. +It will likely end up in a memory type that is `HOST_VISIBLE` and `HOST_COHERENT` +but not `HOST_CACHED` (meaning uncached, write-combined) and not `DEVICE_LOCAL` (meaning system RAM). + +\code +VkBufferCreateInfo stagingBufferInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +stagingBufferInfo.size = 65536; +stagingBufferInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + +VmaAllocationCreateInfo stagingAllocInfo = {}; +stagingAllocInfo.usage = VMA_MEMORY_USAGE_AUTO; +stagingAllocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT; + +VkBuffer stagingBuffer; +VmaAllocation stagingAllocation; +vmaCreateBuffer(allocator, &stagingBufferInfo, &stagingAllocInfo, &stagingBuffer, &stagingAllocation, nullptr); +\endcode + +For more examples of creating different kinds of resources, see chapter \ref usage_patterns. + +Usage values `VMA_MEMORY_USAGE_AUTO*` are legal to use only when the library knows +about the resource being created by having `VkBufferCreateInfo` / `VkImageCreateInfo` passed, +so they work with functions like: vmaCreateBuffer(), vmaCreateImage(), vmaFindMemoryTypeIndexForBufferInfo() etc. +If you allocate raw memory using function vmaAllocateMemory(), you have to use other means of selecting +memory type, as described below. + +\note +Old usage values (`VMA_MEMORY_USAGE_GPU_ONLY`, `VMA_MEMORY_USAGE_CPU_ONLY`, +`VMA_MEMORY_USAGE_CPU_TO_GPU`, `VMA_MEMORY_USAGE_GPU_TO_CPU`, `VMA_MEMORY_USAGE_CPU_COPY`) +are still available and work same way as in previous versions of the library +for backward compatibility, but they are not recommended. + +\section choosing_memory_type_required_preferred_flags Required and preferred flags + +You can specify more detailed requirements by filling members +VmaAllocationCreateInfo::requiredFlags and VmaAllocationCreateInfo::preferredFlags +with a combination of bits from enum `VkMemoryPropertyFlags`. For example, +if you want to create a buffer that will be persistently mapped on host (so it +must be `HOST_VISIBLE`) and preferably will also be `HOST_COHERENT` and `HOST_CACHED`, +use following code: + +\code +VmaAllocationCreateInfo allocInfo = {}; +allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT; +allocInfo.preferredFlags = VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT; +allocInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT; + +VkBuffer buffer; +VmaAllocation allocation; +vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); +\endcode + +A memory type is chosen that has all the required flags and as many preferred +flags set as possible. + +Value passed in VmaAllocationCreateInfo::usage is internally converted to a set of required and preferred flags, +plus some extra "magic" (heuristics). + +\section choosing_memory_type_explicit_memory_types Explicit memory types + +If you inspected memory types available on the physical device and you have +a preference for memory types that you want to use, you can fill member +VmaAllocationCreateInfo::memoryTypeBits. It is a bit mask, where each bit set +means that a memory type with that index is allowed to be used for the +allocation. Special value 0, just like `UINT32_MAX`, means there are no +restrictions to memory type index. + +Please note that this member is NOT just a memory type index. +Still you can use it to choose just one, specific memory type. +For example, if you already determined that your buffer should be created in +memory type 2, use following code: + +\code +uint32_t memoryTypeIndex = 2; + +VmaAllocationCreateInfo allocInfo = {}; +allocInfo.memoryTypeBits = 1u << memoryTypeIndex; + +VkBuffer buffer; +VmaAllocation allocation; +vmaCreateBuffer(allocator, &bufferInfo, &allocInfo, &buffer, &allocation, nullptr); +\endcode + + +\section choosing_memory_type_custom_memory_pools Custom memory pools + +If you allocate from custom memory pool, all the ways of specifying memory +requirements described above are not applicable and the aforementioned members +of VmaAllocationCreateInfo structure are ignored. Memory type is selected +explicitly when creating the pool and then used to make all the allocations from +that pool. For further details, see \ref custom_memory_pools. + +\section choosing_memory_type_dedicated_allocations Dedicated allocations + +Memory for allocations is reserved out of larger block of `VkDeviceMemory` +allocated from Vulkan internally. That is the main feature of this whole library. +You can still request a separate memory block to be created for an allocation, +just like you would do in a trivial solution without using any allocator. +In that case, a buffer or image is always bound to that memory at offset 0. +This is called a "dedicated allocation". +You can explicitly request it by using flag #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. +The library can also internally decide to use dedicated allocation in some cases, e.g.: + +- When the size of the allocation is large. +- When [VK_KHR_dedicated_allocation](@ref vk_khr_dedicated_allocation) extension is enabled + and it reports that dedicated allocation is required or recommended for the resource. +- When allocation of next big memory block fails due to not enough device memory, + but allocation with the exact requested size succeeds. + + +\page memory_mapping Memory mapping + +To "map memory" in Vulkan means to obtain a CPU pointer to `VkDeviceMemory`, +to be able to read from it or write to it in CPU code. +Mapping is possible only of memory allocated from a memory type that has +`VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` flag. +Functions `vkMapMemory()`, `vkUnmapMemory()` are designed for this purpose. +You can use them directly with memory allocated by this library, +but it is not recommended because of following issue: +Mapping the same `VkDeviceMemory` block multiple times is illegal - only one mapping at a time is allowed. +This includes mapping disjoint regions. Mapping is not reference-counted internally by Vulkan. +Because of this, Vulkan Memory Allocator provides following facilities: + +\note If you want to be able to map an allocation, you need to specify one of the flags +#VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT +in VmaAllocationCreateInfo::flags. These flags are required for an allocation to be mappable +when using #VMA_MEMORY_USAGE_AUTO or other `VMA_MEMORY_USAGE_AUTO*` enum values. +For other usage values they are ignored and every such allocation made in `HOST_VISIBLE` memory type is mappable, +but they can still be used for consistency. + +\section memory_mapping_mapping_functions Mapping functions + +The library provides following functions for mapping of a specific #VmaAllocation: vmaMapMemory(), vmaUnmapMemory(). +They are safer and more convenient to use than standard Vulkan functions. +You can map an allocation multiple times simultaneously - mapping is reference-counted internally. +You can also map different allocations simultaneously regardless of whether they use the same `VkDeviceMemory` block. +The way it is implemented is that the library always maps entire memory block, not just region of the allocation. +For further details, see description of vmaMapMemory() function. +Example: + +\code +// Having these objects initialized: +struct ConstantBuffer +{ + ... +}; +ConstantBuffer constantBufferData = ... + +VmaAllocator allocator = ... +VkBuffer constantBuffer = ... +VmaAllocation constantBufferAllocation = ... + +// You can map and fill your buffer using following code: + +void* mappedData; +vmaMapMemory(allocator, constantBufferAllocation, &mappedData); +memcpy(mappedData, &constantBufferData, sizeof(constantBufferData)); +vmaUnmapMemory(allocator, constantBufferAllocation); +\endcode + +When mapping, you may see a warning from Vulkan validation layer similar to this one: + +Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used. + +It happens because the library maps entire `VkDeviceMemory` block, where different +types of images and buffers may end up together, especially on GPUs with unified memory like Intel. +You can safely ignore it if you are sure you access only memory of the intended +object that you wanted to map. + + +\section memory_mapping_persistently_mapped_memory Persistently mapped memory + +Keeping your memory persistently mapped is generally OK in Vulkan. +You don't need to unmap it before using its data on the GPU. +The library provides a special feature designed for that: +Allocations made with #VMA_ALLOCATION_CREATE_MAPPED_BIT flag set in +VmaAllocationCreateInfo::flags stay mapped all the time, +so you can just access CPU pointer to it any time +without a need to call any "map" or "unmap" function. +Example: + +\code +VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufCreateInfo.size = sizeof(ConstantBuffer); +bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; +allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | + VMA_ALLOCATION_CREATE_MAPPED_BIT; + +VkBuffer buf; +VmaAllocation alloc; +VmaAllocationInfo allocInfo; +vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); + +// Buffer is already mapped. You can access its memory. +memcpy(allocInfo.pMappedData, &constantBufferData, sizeof(constantBufferData)); +\endcode + +\note #VMA_ALLOCATION_CREATE_MAPPED_BIT by itself doesn't guarantee that the allocation will end up +in a mappable memory type. +For this, you need to also specify #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT or +#VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT. +#VMA_ALLOCATION_CREATE_MAPPED_BIT only guarantees that if the memory is `HOST_VISIBLE`, the allocation will be mapped on creation. +For an example of how to make use of this fact, see section \ref usage_patterns_advanced_data_uploading. + +\section memory_mapping_cache_control Cache flush and invalidate + +Memory in Vulkan doesn't need to be unmapped before using it on GPU, +but unless a memory types has `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` flag set, +you need to manually **invalidate** cache before reading of mapped pointer +and **flush** cache after writing to mapped pointer. +Map/unmap operations don't do that automatically. +Vulkan provides following functions for this purpose `vkFlushMappedMemoryRanges()`, +`vkInvalidateMappedMemoryRanges()`, but this library provides more convenient +functions that refer to given allocation object: vmaFlushAllocation(), +vmaInvalidateAllocation(), +or multiple objects at once: vmaFlushAllocations(), vmaInvalidateAllocations(). + +Regions of memory specified for flush/invalidate must be aligned to +`VkPhysicalDeviceLimits::nonCoherentAtomSize`. This is automatically ensured by the library. +In any memory type that is `HOST_VISIBLE` but not `HOST_COHERENT`, all allocations +within blocks are aligned to this value, so their offsets are always multiply of +`nonCoherentAtomSize` and two different allocations never share same "line" of this size. + +Also, Windows drivers from all 3 PC GPU vendors (AMD, Intel, NVIDIA) +currently provide `HOST_COHERENT` flag on all memory types that are +`HOST_VISIBLE`, so on PC you may not need to bother. + + +\page staying_within_budget Staying within budget + +When developing a graphics-intensive game or program, it is important to avoid allocating +more GPU memory than it is physically available. When the memory is over-committed, +various bad things can happen, depending on the specific GPU, graphics driver, and +operating system: + +- It may just work without any problems. +- The application may slow down because some memory blocks are moved to system RAM + and the GPU has to access them through PCI Express bus. +- A new allocation may take very long time to complete, even few seconds, and possibly + freeze entire system. +- The new allocation may fail with `VK_ERROR_OUT_OF_DEVICE_MEMORY`. +- It may even result in GPU crash (TDR), observed as `VK_ERROR_DEVICE_LOST` + returned somewhere later. + +\section staying_within_budget_querying_for_budget Querying for budget + +To query for current memory usage and available budget, use function vmaGetHeapBudgets(). +Returned structure #VmaBudget contains quantities expressed in bytes, per Vulkan memory heap. + +Please note that this function returns different information and works faster than +vmaCalculateStatistics(). vmaGetHeapBudgets() can be called every frame or even before every +allocation, while vmaCalculateStatistics() is intended to be used rarely, +only to obtain statistical information, e.g. for debugging purposes. + +It is recommended to use VK_EXT_memory_budget device extension to obtain information +about the budget from Vulkan device. VMA is able to use this extension automatically. +When not enabled, the allocator behaves same way, but then it estimates current usage +and available budget based on its internal information and Vulkan memory heap sizes, +which may be less precise. In order to use this extension: + +1. Make sure extensions VK_EXT_memory_budget and VK_KHR_get_physical_device_properties2 + required by it are available and enable them. Please note that the first is a device + extension and the second is instance extension! +2. Use flag #VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT when creating #VmaAllocator object. +3. Make sure to call vmaSetCurrentFrameIndex() every frame. Budget is queried from + Vulkan inside of it to avoid overhead of querying it with every allocation. + +\section staying_within_budget_controlling_memory_usage Controlling memory usage + +There are many ways in which you can try to stay within the budget. + +First, when making new allocation requires allocating a new memory block, the library +tries not to exceed the budget automatically. If a block with default recommended size +(e.g. 256 MB) would go over budget, a smaller block is allocated, possibly even +dedicated memory for just this resource. + +If the size of the requested resource plus current memory usage is more than the +budget, by default the library still tries to create it, leaving it to the Vulkan +implementation whether the allocation succeeds or fails. You can change this behavior +by using #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag. With it, the allocation is +not made if it would exceed the budget or if the budget is already exceeded. +VMA then tries to make the allocation from the next eligible Vulkan memory type. +The all of them fail, the call then fails with `VK_ERROR_OUT_OF_DEVICE_MEMORY`. +Example usage pattern may be to pass the #VMA_ALLOCATION_CREATE_WITHIN_BUDGET_BIT flag +when creating resources that are not essential for the application (e.g. the texture +of a specific object) and not to pass it when creating critically important resources +(e.g. render targets). + +On AMD graphics cards there is a custom vendor extension available: VK_AMD_memory_overallocation_behavior +that allows to control the behavior of the Vulkan implementation in out-of-memory cases - +whether it should fail with an error code or still allow the allocation. +Usage of this extension involves only passing extra structure on Vulkan device creation, +so it is out of scope of this library. + +Finally, you can also use #VMA_ALLOCATION_CREATE_NEVER_ALLOCATE_BIT flag to make sure +a new allocation is created only when it fits inside one of the existing memory blocks. +If it would require to allocate a new block, if fails instead with `VK_ERROR_OUT_OF_DEVICE_MEMORY`. +This also ensures that the function call is very fast because it never goes to Vulkan +to obtain a new block. + +\note Creating \ref custom_memory_pools with VmaPoolCreateInfo::minBlockCount +set to more than 0 will currently try to allocate memory blocks without checking whether they +fit within budget. + + +\page resource_aliasing Resource aliasing (overlap) + +New explicit graphics APIs (Vulkan and Direct3D 12), thanks to manual memory +management, give an opportunity to alias (overlap) multiple resources in the +same region of memory - a feature not available in the old APIs (Direct3D 11, OpenGL). +It can be useful to save video memory, but it must be used with caution. + +For example, if you know the flow of your whole render frame in advance, you +are going to use some intermediate textures or buffers only during a small range of render passes, +and you know these ranges don't overlap in time, you can bind these resources to +the same place in memory, even if they have completely different parameters (width, height, format etc.). + +![Resource aliasing (overlap)](../gfx/Aliasing.png) + +Such scenario is possible using VMA, but you need to create your images manually. +Then you need to calculate parameters of an allocation to be made using formula: + +- allocation size = max(size of each image) +- allocation alignment = max(alignment of each image) +- allocation memoryTypeBits = bitwise AND(memoryTypeBits of each image) + +Following example shows two different images bound to the same place in memory, +allocated to fit largest of them. + +\code +// A 512x512 texture to be sampled. +VkImageCreateInfo img1CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; +img1CreateInfo.imageType = VK_IMAGE_TYPE_2D; +img1CreateInfo.extent.width = 512; +img1CreateInfo.extent.height = 512; +img1CreateInfo.extent.depth = 1; +img1CreateInfo.mipLevels = 10; +img1CreateInfo.arrayLayers = 1; +img1CreateInfo.format = VK_FORMAT_R8G8B8A8_SRGB; +img1CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; +img1CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; +img1CreateInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; +img1CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + +// A full screen texture to be used as color attachment. +VkImageCreateInfo img2CreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; +img2CreateInfo.imageType = VK_IMAGE_TYPE_2D; +img2CreateInfo.extent.width = 1920; +img2CreateInfo.extent.height = 1080; +img2CreateInfo.extent.depth = 1; +img2CreateInfo.mipLevels = 1; +img2CreateInfo.arrayLayers = 1; +img2CreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM; +img2CreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; +img2CreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; +img2CreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; +img2CreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + +VkImage img1; +res = vkCreateImage(device, &img1CreateInfo, nullptr, &img1); +VkImage img2; +res = vkCreateImage(device, &img2CreateInfo, nullptr, &img2); + +VkMemoryRequirements img1MemReq; +vkGetImageMemoryRequirements(device, img1, &img1MemReq); +VkMemoryRequirements img2MemReq; +vkGetImageMemoryRequirements(device, img2, &img2MemReq); + +VkMemoryRequirements finalMemReq = {}; +finalMemReq.size = std::max(img1MemReq.size, img2MemReq.size); +finalMemReq.alignment = std::max(img1MemReq.alignment, img2MemReq.alignment); +finalMemReq.memoryTypeBits = img1MemReq.memoryTypeBits & img2MemReq.memoryTypeBits; +// Validate if(finalMemReq.memoryTypeBits != 0) + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + +VmaAllocation alloc; +res = vmaAllocateMemory(allocator, &finalMemReq, &allocCreateInfo, &alloc, nullptr); + +res = vmaBindImageMemory(allocator, alloc, img1); +res = vmaBindImageMemory(allocator, alloc, img2); + +// You can use img1, img2 here, but not at the same time! + +vmaFreeMemory(allocator, alloc); +vkDestroyImage(allocator, img2, nullptr); +vkDestroyImage(allocator, img1, nullptr); +\endcode + +Remember that using resources that alias in memory requires proper synchronization. +You need to issue a memory barrier to make sure commands that use `img1` and `img2` +don't overlap on GPU timeline. +You also need to treat a resource after aliasing as uninitialized - containing garbage data. +For example, if you use `img1` and then want to use `img2`, you need to issue +an image memory barrier for `img2` with `oldLayout` = `VK_IMAGE_LAYOUT_UNDEFINED`. + +Additional considerations: + +- Vulkan also allows to interpret contents of memory between aliasing resources consistently in some cases. +See chapter 11.8. "Memory Aliasing" of Vulkan specification or `VK_IMAGE_CREATE_ALIAS_BIT` flag. +- You can create more complex layout where different images and buffers are bound +at different offsets inside one large allocation. For example, one can imagine +a big texture used in some render passes, aliasing with a set of many small buffers +used between in some further passes. To bind a resource at non-zero offset in an allocation, +use vmaBindBufferMemory2() / vmaBindImageMemory2(). +- Before allocating memory for the resources you want to alias, check `memoryTypeBits` +returned in memory requirements of each resource to make sure the bits overlap. +Some GPUs may expose multiple memory types suitable e.g. only for buffers or +images with `COLOR_ATTACHMENT` usage, so the sets of memory types supported by your +resources may be disjoint. Aliasing them is not possible in that case. + + +\page custom_memory_pools Custom memory pools + +A memory pool contains a number of `VkDeviceMemory` blocks. +The library automatically creates and manages default pool for each memory type available on the device. +Default memory pool automatically grows in size. +Size of allocated blocks is also variable and managed automatically. + +You can create custom pool and allocate memory out of it. +It can be useful if you want to: + +- Keep certain kind of allocations separate from others. +- Enforce particular, fixed size of Vulkan memory blocks. +- Limit maximum amount of Vulkan memory allocated for that pool. +- Reserve minimum or fixed amount of Vulkan memory always preallocated for that pool. +- Use extra parameters for a set of your allocations that are available in #VmaPoolCreateInfo but not in + #VmaAllocationCreateInfo - e.g., custom minimum alignment, custom `pNext` chain. +- Perform defragmentation on a specific subset of your allocations. + +To use custom memory pools: + +-# Fill VmaPoolCreateInfo structure. +-# Call vmaCreatePool() to obtain #VmaPool handle. +-# When making an allocation, set VmaAllocationCreateInfo::pool to this handle. + You don't need to specify any other parameters of this structure, like `usage`. + +Example: + +\code +// Find memoryTypeIndex for the pool. +VkBufferCreateInfo sampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +sampleBufCreateInfo.size = 0x10000; // Doesn't matter. +sampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo sampleAllocCreateInfo = {}; +sampleAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; + +uint32_t memTypeIndex; +VkResult res = vmaFindMemoryTypeIndexForBufferInfo(allocator, + &sampleBufCreateInfo, &sampleAllocCreateInfo, &memTypeIndex); +// Check res... + +// Create a pool that can have at most 2 blocks, 128 MiB each. +VmaPoolCreateInfo poolCreateInfo = {}; +poolCreateInfo.memoryTypeIndex = memTypeIndex; +poolCreateInfo.blockSize = 128ull * 1024 * 1024; +poolCreateInfo.maxBlockCount = 2; + +VmaPool pool; +res = vmaCreatePool(allocator, &poolCreateInfo, &pool); +// Check res... + +// Allocate a buffer out of it. +VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufCreateInfo.size = 1024; +bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.pool = pool; + +VkBuffer buf; +VmaAllocation alloc; +res = vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, nullptr); +// Check res... +\endcode + +You have to free all allocations made from this pool before destroying it. + +\code +vmaDestroyBuffer(allocator, buf, alloc); +vmaDestroyPool(allocator, pool); +\endcode + +New versions of this library support creating dedicated allocations in custom pools. +It is supported only when VmaPoolCreateInfo::blockSize = 0. +To use this feature, set VmaAllocationCreateInfo::pool to the pointer to your custom pool and +VmaAllocationCreateInfo::flags to #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. + +\note Excessive use of custom pools is a common mistake when using this library. +Custom pools may be useful for special purposes - when you want to +keep certain type of resources separate e.g. to reserve minimum amount of memory +for them or limit maximum amount of memory they can occupy. For most +resources this is not needed and so it is not recommended to create #VmaPool +objects and allocations out of them. Allocating from the default pool is sufficient. + + +\section custom_memory_pools_MemTypeIndex Choosing memory type index + +When creating a pool, you must explicitly specify memory type index. +To find the one suitable for your buffers or images, you can use helper functions +vmaFindMemoryTypeIndexForBufferInfo(), vmaFindMemoryTypeIndexForImageInfo(). +You need to provide structures with example parameters of buffers or images +that you are going to create in that pool. + +\code +VkBufferCreateInfo exampleBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +exampleBufCreateInfo.size = 1024; // Doesn't matter +exampleBufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; + +uint32_t memTypeIndex; +vmaFindMemoryTypeIndexForBufferInfo(allocator, &exampleBufCreateInfo, &allocCreateInfo, &memTypeIndex); + +VmaPoolCreateInfo poolCreateInfo = {}; +poolCreateInfo.memoryTypeIndex = memTypeIndex; +// ... +\endcode + +When creating buffers/images allocated in that pool, provide following parameters: + +- `VkBufferCreateInfo`: Prefer to pass same parameters as above. + Otherwise you risk creating resources in a memory type that is not suitable for them, which may result in undefined behavior. + Using different `VK_BUFFER_USAGE_` flags may work, but you shouldn't create images in a pool intended for buffers + or the other way around. +- VmaAllocationCreateInfo: You don't need to pass same parameters. Fill only `pool` member. + Other members are ignored anyway. + +\section linear_algorithm Linear allocation algorithm + +Each Vulkan memory block managed by this library has accompanying metadata that +keeps track of used and unused regions. By default, the metadata structure and +algorithm tries to find best place for new allocations among free regions to +optimize memory usage. This way you can allocate and free objects in any order. + +![Default allocation algorithm](../gfx/Linear_allocator_1_algo_default.png) + +Sometimes there is a need to use simpler, linear allocation algorithm. You can +create custom pool that uses such algorithm by adding flag +#VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT to VmaPoolCreateInfo::flags while creating +#VmaPool object. Then an alternative metadata management is used. It always +creates new allocations after last one and doesn't reuse free regions after +allocations freed in the middle. It results in better allocation performance and +less memory consumed by metadata. + +![Linear allocation algorithm](../gfx/Linear_allocator_2_algo_linear.png) + +With this one flag, you can create a custom pool that can be used in many ways: +free-at-once, stack, double stack, and ring buffer. See below for details. +You don't need to specify explicitly which of these options you are going to use - it is detected automatically. + +\subsection linear_algorithm_free_at_once Free-at-once + +In a pool that uses linear algorithm, you still need to free all the allocations +individually, e.g. by using vmaFreeMemory() or vmaDestroyBuffer(). You can free +them in any order. New allocations are always made after last one - free space +in the middle is not reused. However, when you release all the allocation and +the pool becomes empty, allocation starts from the beginning again. This way you +can use linear algorithm to speed up creation of allocations that you are going +to release all at once. + +![Free-at-once](../gfx/Linear_allocator_3_free_at_once.png) + +This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount +value that allows multiple memory blocks. + +\subsection linear_algorithm_stack Stack + +When you free an allocation that was created last, its space can be reused. +Thanks to this, if you always release allocations in the order opposite to their +creation (LIFO - Last In First Out), you can achieve behavior of a stack. + +![Stack](../gfx/Linear_allocator_4_stack.png) + +This mode is also available for pools created with VmaPoolCreateInfo::maxBlockCount +value that allows multiple memory blocks. + +\subsection linear_algorithm_double_stack Double stack + +The space reserved by a custom pool with linear algorithm may be used by two +stacks: + +- First, default one, growing up from offset 0. +- Second, "upper" one, growing down from the end towards lower offsets. + +To make allocation from the upper stack, add flag #VMA_ALLOCATION_CREATE_UPPER_ADDRESS_BIT +to VmaAllocationCreateInfo::flags. + +![Double stack](../gfx/Linear_allocator_7_double_stack.png) + +Double stack is available only in pools with one memory block - +VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined. + +When the two stacks' ends meet so there is not enough space between them for a +new allocation, such allocation fails with usual +`VK_ERROR_OUT_OF_DEVICE_MEMORY` error. + +\subsection linear_algorithm_ring_buffer Ring buffer + +When you free some allocations from the beginning and there is not enough free space +for a new one at the end of a pool, allocator's "cursor" wraps around to the +beginning and starts allocation there. Thanks to this, if you always release +allocations in the same order as you created them (FIFO - First In First Out), +you can achieve behavior of a ring buffer / queue. + +![Ring buffer](../gfx/Linear_allocator_5_ring_buffer.png) + +Ring buffer is available only in pools with one memory block - +VmaPoolCreateInfo::maxBlockCount must be 1. Otherwise behavior is undefined. + +\note \ref defragmentation is not supported in custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT. + + +\page defragmentation Defragmentation + +Interleaved allocations and deallocations of many objects of varying size can +cause fragmentation over time, which can lead to a situation where the library is unable +to find a continuous range of free memory for a new allocation despite there is +enough free space, just scattered across many small free ranges between existing +allocations. + +To mitigate this problem, you can use defragmentation feature. +It doesn't happen automatically though and needs your cooperation, +because VMA is a low level library that only allocates memory. +It cannot recreate buffers and images in a new place as it doesn't remember the contents of `VkBufferCreateInfo` / `VkImageCreateInfo` structures. +It cannot copy their contents as it doesn't record any commands to a command buffer. + +Example: + +\code +VmaDefragmentationInfo defragInfo = {}; +defragInfo.pool = myPool; +defragInfo.flags = VMA_DEFRAGMENTATION_FLAG_ALGORITHM_FAST_BIT; + +VmaDefragmentationContext defragCtx; +VkResult res = vmaBeginDefragmentation(allocator, &defragInfo, &defragCtx); +// Check res... + +for(;;) +{ + VmaDefragmentationPassMoveInfo pass; + res = vmaBeginDefragmentationPass(allocator, defragCtx, &pass); + if(res == VK_SUCCESS) + break; + else if(res != VK_INCOMPLETE) + // Handle error... + + for(uint32_t i = 0; i < pass.moveCount; ++i) + { + // Inspect pass.pMoves[i].srcAllocation, identify what buffer/image it represents. + VmaAllocationInfo allocInfo; + vmaGetAllocationInfo(allocator, pass.pMoves[i].srcAllocation, &allocInfo); + MyEngineResourceData* resData = (MyEngineResourceData*)allocInfo.pUserData; + + // Recreate and bind this buffer/image at: pass.pMoves[i].dstMemory, pass.pMoves[i].dstOffset. + VkImageCreateInfo imgCreateInfo = ... + VkImage newImg; + res = vkCreateImage(device, &imgCreateInfo, nullptr, &newImg); + // Check res... + res = vmaBindImageMemory(allocator, pass.pMoves[i].dstTmpAllocation, newImg); + // Check res... + + // Issue a vkCmdCopyBuffer/vkCmdCopyImage to copy its content to the new place. + vkCmdCopyImage(cmdBuf, resData->img, ..., newImg, ...); + } + + // Make sure the copy commands finished executing. + vkWaitForFences(...); + + // Destroy old buffers/images bound with pass.pMoves[i].srcAllocation. + for(uint32_t i = 0; i < pass.moveCount; ++i) + { + // ... + vkDestroyImage(device, resData->img, nullptr); + } + + // Update appropriate descriptors to point to the new places... + + res = vmaEndDefragmentationPass(allocator, defragCtx, &pass); + if(res == VK_SUCCESS) + break; + else if(res != VK_INCOMPLETE) + // Handle error... +} + +vmaEndDefragmentation(allocator, defragCtx, nullptr); +\endcode + +Although functions like vmaCreateBuffer(), vmaCreateImage(), vmaDestroyBuffer(), vmaDestroyImage() +create/destroy an allocation and a buffer/image at once, these are just a shortcut for +creating the resource, allocating memory, and binding them together. +Defragmentation works on memory allocations only. You must handle the rest manually. +Defragmentation is an iterative process that should repreat "passes" as long as related functions +return `VK_INCOMPLETE` not `VK_SUCCESS`. +In each pass: + +1. vmaBeginDefragmentationPass() function call: + - Calculates and returns the list of allocations to be moved in this pass. + Note this can be a time-consuming process. + - Reserves destination memory for them by creating temporary destination allocations + that you can query for their `VkDeviceMemory` + offset using vmaGetAllocationInfo(). +2. Inside the pass, **you should**: + - Inspect the returned list of allocations to be moved. + - Create new buffers/images and bind them at the returned destination temporary allocations. + - Copy data from source to destination resources if necessary. + - Destroy the source buffers/images, but NOT their allocations. +3. vmaEndDefragmentationPass() function call: + - Frees the source memory reserved for the allocations that are moved. + - Modifies source #VmaAllocation objects that are moved to point to the destination reserved memory. + - Frees `VkDeviceMemory` blocks that became empty. + +Unlike in previous iterations of the defragmentation API, there is no list of "movable" allocations passed as a parameter. +Defragmentation algorithm tries to move all suitable allocations. +You can, however, refuse to move some of them inside a defragmentation pass, by setting +`pass.pMoves[i].operation` to #VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE. +This is not recommended and may result in suboptimal packing of the allocations after defragmentation. +If you cannot ensure any allocation can be moved, it is better to keep movable allocations separate in a custom pool. + +Inside a pass, for each allocation that should be moved: + +- You should copy its data from the source to the destination place by calling e.g. `vkCmdCopyBuffer()`, `vkCmdCopyImage()`. + - You need to make sure these commands finished executing before destroying the source buffers/images and before calling vmaEndDefragmentationPass(). +- If a resource doesn't contain any meaningful data, e.g. it is a transient color attachment image to be cleared, + filled, and used temporarily in each rendering frame, you can just recreate this image + without copying its data. +- If the resource is in `HOST_VISIBLE` and `HOST_CACHED` memory, you can copy its data on the CPU + using `memcpy()`. +- If you cannot move the allocation, you can set `pass.pMoves[i].operation` to #VMA_DEFRAGMENTATION_MOVE_OPERATION_IGNORE. + This will cancel the move. + - vmaEndDefragmentationPass() will then free the destination memory + not the source memory of the allocation, leaving it unchanged. +- If you decide the allocation is unimportant and can be destroyed instead of moved (e.g. it wasn't used for long time), + you can set `pass.pMoves[i].operation` to #VMA_DEFRAGMENTATION_MOVE_OPERATION_DESTROY. + - vmaEndDefragmentationPass() will then free both source and destination memory, and will destroy the source #VmaAllocation object. + +You can defragment a specific custom pool by setting VmaDefragmentationInfo::pool +(like in the example above) or all the default pools by setting this member to null. + +Defragmentation is always performed in each pool separately. +Allocations are never moved between different Vulkan memory types. +The size of the destination memory reserved for a moved allocation is the same as the original one. +Alignment of an allocation as it was determined using `vkGetBufferMemoryRequirements()` etc. is also respected after defragmentation. +Buffers/images should be recreated with the same `VkBufferCreateInfo` / `VkImageCreateInfo` parameters as the original ones. + +You can perform the defragmentation incrementally to limit the number of allocations and bytes to be moved +in each pass, e.g. to call it in sync with render frames and not to experience too big hitches. +See members: VmaDefragmentationInfo::maxBytesPerPass, VmaDefragmentationInfo::maxAllocationsPerPass. + +It is also safe to perform the defragmentation asynchronously to render frames and other Vulkan and VMA +usage, possibly from multiple threads, with the exception that allocations +returned in VmaDefragmentationPassMoveInfo::pMoves shouldn't be destroyed until the defragmentation pass is ended. + +Mapping is preserved on allocations that are moved during defragmentation. +Whether through #VMA_ALLOCATION_CREATE_MAPPED_BIT or vmaMapMemory(), the allocations +are mapped at their new place. Of course, pointer to the mapped data changes, so it needs to be queried +using VmaAllocationInfo::pMappedData. + +\note Defragmentation is not supported in custom pools created with #VMA_POOL_CREATE_LINEAR_ALGORITHM_BIT. + + +\page statistics Statistics + +This library contains several functions that return information about its internal state, +especially the amount of memory allocated from Vulkan. + +\section statistics_numeric_statistics Numeric statistics + +If you need to obtain basic statistics about memory usage per heap, together with current budget, +you can call function vmaGetHeapBudgets() and inspect structure #VmaBudget. +This is useful to keep track of memory usage and stay within budget +(see also \ref staying_within_budget). +Example: + +\code +uint32_t heapIndex = ... + +VmaBudget budgets[VK_MAX_MEMORY_HEAPS]; +vmaGetHeapBudgets(allocator, budgets); + +printf("My heap currently has %u allocations taking %llu B,\n", + budgets[heapIndex].statistics.allocationCount, + budgets[heapIndex].statistics.allocationBytes); +printf("allocated out of %u Vulkan device memory blocks taking %llu B,\n", + budgets[heapIndex].statistics.blockCount, + budgets[heapIndex].statistics.blockBytes); +printf("Vulkan reports total usage %llu B with budget %llu B.\n", + budgets[heapIndex].usage, + budgets[heapIndex].budget); +\endcode + +You can query for more detailed statistics per memory heap, type, and totals, +including minimum and maximum allocation size and unused range size, +by calling function vmaCalculateStatistics() and inspecting structure #VmaTotalStatistics. +This function is slower though, as it has to traverse all the internal data structures, +so it should be used only for debugging purposes. + +You can query for statistics of a custom pool using function vmaGetPoolStatistics() +or vmaCalculatePoolStatistics(). + +You can query for information about a specific allocation using function vmaGetAllocationInfo(). +It fill structure #VmaAllocationInfo. + +\section statistics_json_dump JSON dump + +You can dump internal state of the allocator to a string in JSON format using function vmaBuildStatsString(). +The result is guaranteed to be correct JSON. +It uses ANSI encoding. +Any strings provided by user (see [Allocation names](@ref allocation_names)) +are copied as-is and properly escaped for JSON, so if they use UTF-8, ISO-8859-2 or any other encoding, +this JSON string can be treated as using this encoding. +It must be freed using function vmaFreeStatsString(). + +The format of this JSON string is not part of official documentation of the library, +but it will not change in backward-incompatible way without increasing library major version number +and appropriate mention in changelog. + +The JSON string contains all the data that can be obtained using vmaCalculateStatistics(). +It can also contain detailed map of allocated memory blocks and their regions - +free and occupied by allocations. +This allows e.g. to visualize the memory or assess fragmentation. + + +\page allocation_annotation Allocation names and user data + +\section allocation_user_data Allocation user data + +You can annotate allocations with your own information, e.g. for debugging purposes. +To do that, fill VmaAllocationCreateInfo::pUserData field when creating +an allocation. It is an opaque `void*` pointer. You can use it e.g. as a pointer, +some handle, index, key, ordinal number or any other value that would associate +the allocation with your custom metadata. +It is useful to identify appropriate data structures in your engine given #VmaAllocation, +e.g. when doing \ref defragmentation. + +\code +VkBufferCreateInfo bufCreateInfo = ... + +MyBufferMetadata* pMetadata = CreateBufferMetadata(); + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; +allocCreateInfo.pUserData = pMetadata; + +VkBuffer buffer; +VmaAllocation allocation; +vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buffer, &allocation, nullptr); +\endcode + +The pointer may be later retrieved as VmaAllocationInfo::pUserData: + +\code +VmaAllocationInfo allocInfo; +vmaGetAllocationInfo(allocator, allocation, &allocInfo); +MyBufferMetadata* pMetadata = (MyBufferMetadata*)allocInfo.pUserData; +\endcode + +It can also be changed using function vmaSetAllocationUserData(). + +Values of (non-zero) allocations' `pUserData` are printed in JSON report created by +vmaBuildStatsString() in hexadecimal form. + +\section allocation_names Allocation names + +An allocation can also carry a null-terminated string, giving a name to the allocation. +To set it, call vmaSetAllocationName(). +The library creates internal copy of the string, so the pointer you pass doesn't need +to be valid for whole lifetime of the allocation. You can free it after the call. + +\code +std::string imageName = "Texture: "; +imageName += fileName; +vmaSetAllocationName(allocator, allocation, imageName.c_str()); +\endcode + +The string can be later retrieved by inspecting VmaAllocationInfo::pName. +It is also printed in JSON report created by vmaBuildStatsString(). + +\note Setting string name to VMA allocation doesn't automatically set it to the Vulkan buffer or image created with it. +You must do it manually using an extension like VK_EXT_debug_utils, which is independent of this library. + + +\page virtual_allocator Virtual allocator + +As an extra feature, the core allocation algorithm of the library is exposed through a simple and convenient API of "virtual allocator". +It doesn't allocate any real GPU memory. It just keeps track of used and free regions of a "virtual block". +You can use it to allocate your own memory or other objects, even completely unrelated to Vulkan. +A common use case is sub-allocation of pieces of one large GPU buffer. + +\section virtual_allocator_creating_virtual_block Creating virtual block + +To use this functionality, there is no main "allocator" object. +You don't need to have #VmaAllocator object created. +All you need to do is to create a separate #VmaVirtualBlock object for each block of memory you want to be managed by the allocator: + +-# Fill in #VmaVirtualBlockCreateInfo structure. +-# Call vmaCreateVirtualBlock(). Get new #VmaVirtualBlock object. + +Example: + +\code +VmaVirtualBlockCreateInfo blockCreateInfo = {}; +blockCreateInfo.size = 1048576; // 1 MB + +VmaVirtualBlock block; +VkResult res = vmaCreateVirtualBlock(&blockCreateInfo, &block); +\endcode + +\section virtual_allocator_making_virtual_allocations Making virtual allocations + +#VmaVirtualBlock object contains internal data structure that keeps track of free and occupied regions +using the same code as the main Vulkan memory allocator. +Similarly to #VmaAllocation for standard GPU allocations, there is #VmaVirtualAllocation type +that represents an opaque handle to an allocation within the virtual block. + +In order to make such allocation: + +-# Fill in #VmaVirtualAllocationCreateInfo structure. +-# Call vmaVirtualAllocate(). Get new #VmaVirtualAllocation object that represents the allocation. + You can also receive `VkDeviceSize offset` that was assigned to the allocation. + +Example: + +\code +VmaVirtualAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.size = 4096; // 4 KB + +VmaVirtualAllocation alloc; +VkDeviceSize offset; +res = vmaVirtualAllocate(block, &allocCreateInfo, &alloc, &offset); +if(res == VK_SUCCESS) +{ + // Use the 4 KB of your memory starting at offset. +} +else +{ + // Allocation failed - no space for it could be found. Handle this error! +} +\endcode + +\section virtual_allocator_deallocation Deallocation + +When no longer needed, an allocation can be freed by calling vmaVirtualFree(). +You can only pass to this function an allocation that was previously returned by vmaVirtualAllocate() +called for the same #VmaVirtualBlock. + +When whole block is no longer needed, the block object can be released by calling vmaDestroyVirtualBlock(). +All allocations must be freed before the block is destroyed, which is checked internally by an assert. +However, if you don't want to call vmaVirtualFree() for each allocation, you can use vmaClearVirtualBlock() to free them all at once - +a feature not available in normal Vulkan memory allocator. Example: + +\code +vmaVirtualFree(block, alloc); +vmaDestroyVirtualBlock(block); +\endcode + +\section virtual_allocator_allocation_parameters Allocation parameters + +You can attach a custom pointer to each allocation by using vmaSetVirtualAllocationUserData(). +Its default value is null. +It can be used to store any data that needs to be associated with that allocation - e.g. an index, a handle, or a pointer to some +larger data structure containing more information. Example: + +\code +struct CustomAllocData +{ + std::string m_AllocName; +}; +CustomAllocData* allocData = new CustomAllocData(); +allocData->m_AllocName = "My allocation 1"; +vmaSetVirtualAllocationUserData(block, alloc, allocData); +\endcode + +The pointer can later be fetched, along with allocation offset and size, by passing the allocation handle to function +vmaGetVirtualAllocationInfo() and inspecting returned structure #VmaVirtualAllocationInfo. +If you allocated a new object to be used as the custom pointer, don't forget to delete that object before freeing the allocation! +Example: + +\code +VmaVirtualAllocationInfo allocInfo; +vmaGetVirtualAllocationInfo(block, alloc, &allocInfo); +delete (CustomAllocData*)allocInfo.pUserData; + +vmaVirtualFree(block, alloc); +\endcode + +\section virtual_allocator_alignment_and_units Alignment and units + +It feels natural to express sizes and offsets in bytes. +If an offset of an allocation needs to be aligned to a multiply of some number (e.g. 4 bytes), you can fill optional member +VmaVirtualAllocationCreateInfo::alignment to request it. Example: + +\code +VmaVirtualAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.size = 4096; // 4 KB +allocCreateInfo.alignment = 4; // Returned offset must be a multiply of 4 B + +VmaVirtualAllocation alloc; +res = vmaVirtualAllocate(block, &allocCreateInfo, &alloc, nullptr); +\endcode + +Alignments of different allocations made from one block may vary. +However, if all alignments and sizes are always multiply of some size e.g. 4 B or `sizeof(MyDataStruct)`, +you can express all sizes, alignments, and offsets in multiples of that size instead of individual bytes. +It might be more convenient, but you need to make sure to use this new unit consistently in all the places: + +- VmaVirtualBlockCreateInfo::size +- VmaVirtualAllocationCreateInfo::size and VmaVirtualAllocationCreateInfo::alignment +- Using offset returned by vmaVirtualAllocate() or in VmaVirtualAllocationInfo::offset + +\section virtual_allocator_statistics Statistics + +You can obtain statistics of a virtual block using vmaGetVirtualBlockStatistics() +(to get brief statistics that are fast to calculate) +or vmaCalculateVirtualBlockStatistics() (to get more detailed statistics, slower to calculate). +The functions fill structures #VmaStatistics, #VmaDetailedStatistics respectively - same as used by the normal Vulkan memory allocator. +Example: + +\code +VmaStatistics stats; +vmaGetVirtualBlockStatistics(block, &stats); +printf("My virtual block has %llu bytes used by %u virtual allocations\n", + stats.allocationBytes, stats.allocationCount); +\endcode + +You can also request a full list of allocations and free regions as a string in JSON format by calling +vmaBuildVirtualBlockStatsString(). +Returned string must be later freed using vmaFreeVirtualBlockStatsString(). +The format of this string differs from the one returned by the main Vulkan allocator, but it is similar. + +\section virtual_allocator_additional_considerations Additional considerations + +The "virtual allocator" functionality is implemented on a level of individual memory blocks. +Keeping track of a whole collection of blocks, allocating new ones when out of free space, +deleting empty ones, and deciding which one to try first for a new allocation must be implemented by the user. + +Alternative allocation algorithms are supported, just like in custom pools of the real GPU memory. +See enum #VmaVirtualBlockCreateFlagBits to learn how to specify them (e.g. #VMA_VIRTUAL_BLOCK_CREATE_LINEAR_ALGORITHM_BIT). +You can find their description in chapter \ref custom_memory_pools. +Allocation strategies are also supported. +See enum #VmaVirtualAllocationCreateFlagBits to learn how to specify them (e.g. #VMA_VIRTUAL_ALLOCATION_CREATE_STRATEGY_MIN_TIME_BIT). + +Following features are supported only by the allocator of the real GPU memory and not by virtual allocations: +buffer-image granularity, `VMA_DEBUG_MARGIN`, `VMA_MIN_ALIGNMENT`. + + +\page debugging_memory_usage Debugging incorrect memory usage + +If you suspect a bug with memory usage, like usage of uninitialized memory or +memory being overwritten out of bounds of an allocation, +you can use debug features of this library to verify this. + +\section debugging_memory_usage_initialization Memory initialization + +If you experience a bug with incorrect and nondeterministic data in your program and you suspect uninitialized memory to be used, +you can enable automatic memory initialization to verify this. +To do it, define macro `VMA_DEBUG_INITIALIZE_ALLOCATIONS` to 1. + +\code +#define VMA_DEBUG_INITIALIZE_ALLOCATIONS 1 +#include "vk_mem_alloc.h" +\endcode + +It makes memory of new allocations initialized to bit pattern `0xDCDCDCDC`. +Before an allocation is destroyed, its memory is filled with bit pattern `0xEFEFEFEF`. +Memory is automatically mapped and unmapped if necessary. + +If you find these values while debugging your program, good chances are that you incorrectly +read Vulkan memory that is allocated but not initialized, or already freed, respectively. + +Memory initialization works only with memory types that are `HOST_VISIBLE` and with allocations that can be mapped. +It works also with dedicated allocations. + +\section debugging_memory_usage_margins Margins + +By default, allocations are laid out in memory blocks next to each other if possible +(considering required alignment, `bufferImageGranularity`, and `nonCoherentAtomSize`). + +![Allocations without margin](../gfx/Margins_1.png) + +Define macro `VMA_DEBUG_MARGIN` to some non-zero value (e.g. 16) to enforce specified +number of bytes as a margin after every allocation. + +\code +#define VMA_DEBUG_MARGIN 16 +#include "vk_mem_alloc.h" +\endcode + +![Allocations with margin](../gfx/Margins_2.png) + +If your bug goes away after enabling margins, it means it may be caused by memory +being overwritten outside of allocation boundaries. It is not 100% certain though. +Change in application behavior may also be caused by different order and distribution +of allocations across memory blocks after margins are applied. + +Margins work with all types of memory. + +Margin is applied only to allocations made out of memory blocks and not to dedicated +allocations, which have their own memory block of specific size. +It is thus not applied to allocations made using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT flag +or those automatically decided to put into dedicated allocations, e.g. due to its +large size or recommended by VK_KHR_dedicated_allocation extension. + +Margins appear in [JSON dump](@ref statistics_json_dump) as part of free space. + +Note that enabling margins increases memory usage and fragmentation. + +Margins do not apply to \ref virtual_allocator. + +\section debugging_memory_usage_corruption_detection Corruption detection + +You can additionally define macro `VMA_DEBUG_DETECT_CORRUPTION` to 1 to enable validation +of contents of the margins. + +\code +#define VMA_DEBUG_MARGIN 16 +#define VMA_DEBUG_DETECT_CORRUPTION 1 +#include "vk_mem_alloc.h" +\endcode + +When this feature is enabled, number of bytes specified as `VMA_DEBUG_MARGIN` +(it must be multiply of 4) after every allocation is filled with a magic number. +This idea is also know as "canary". +Memory is automatically mapped and unmapped if necessary. + +This number is validated automatically when the allocation is destroyed. +If it is not equal to the expected value, `VMA_ASSERT()` is executed. +It clearly means that either CPU or GPU overwritten the memory outside of boundaries of the allocation, +which indicates a serious bug. + +You can also explicitly request checking margins of all allocations in all memory blocks +that belong to specified memory types by using function vmaCheckCorruption(), +or in memory blocks that belong to specified custom pool, by using function +vmaCheckPoolCorruption(). + +Margin validation (corruption detection) works only for memory types that are +`HOST_VISIBLE` and `HOST_COHERENT`. + + +\page opengl_interop OpenGL Interop + +VMA provides some features that help with interoperability with OpenGL. + +\section opengl_interop_exporting_memory Exporting memory + +If you want to attach `VkExportMemoryAllocateInfoKHR` structure to `pNext` chain of memory allocations made by the library: + +It is recommended to create \ref custom_memory_pools for such allocations. +Define and fill in your `VkExportMemoryAllocateInfoKHR` structure and attach it to VmaPoolCreateInfo::pMemoryAllocateNext +while creating the custom pool. +Please note that the structure must remain alive and unchanged for the whole lifetime of the #VmaPool, +not only while creating it, as no copy of the structure is made, +but its original pointer is used for each allocation instead. + +If you want to export all memory allocated by the library from certain memory types, +also dedicated allocations or other allocations made from default pools, +an alternative solution is to fill in VmaAllocatorCreateInfo::pTypeExternalMemoryHandleTypes. +It should point to an array with `VkExternalMemoryHandleTypeFlagsKHR` to be automatically passed by the library +through `VkExportMemoryAllocateInfoKHR` on each allocation made from a specific memory type. +Please note that new versions of the library also support dedicated allocations created in custom pools. + +You should not mix these two methods in a way that allows to apply both to the same memory type. +Otherwise, `VkExportMemoryAllocateInfoKHR` structure would be attached twice to the `pNext` chain of `VkMemoryAllocateInfo`. + + +\section opengl_interop_custom_alignment Custom alignment + +Buffers or images exported to a different API like OpenGL may require a different alignment, +higher than the one used by the library automatically, queried from functions like `vkGetBufferMemoryRequirements`. +To impose such alignment: + +It is recommended to create \ref custom_memory_pools for such allocations. +Set VmaPoolCreateInfo::minAllocationAlignment member to the minimum alignment required for each allocation +to be made out of this pool. +The alignment actually used will be the maximum of this member and the alignment returned for the specific buffer or image +from a function like `vkGetBufferMemoryRequirements`, which is called by VMA automatically. + +If you want to create a buffer with a specific minimum alignment out of default pools, +use special function vmaCreateBufferWithAlignment(), which takes additional parameter `minAlignment`. + +Note the problem of alignment affects only resources placed inside bigger `VkDeviceMemory` blocks and not dedicated +allocations, as these, by definition, always have alignment = 0 because the resource is bound to the beginning of its dedicated block. +Contrary to Direct3D 12, Vulkan doesn't have a concept of alignment of the entire memory block passed on its allocation. + + +\page usage_patterns Recommended usage patterns + +Vulkan gives great flexibility in memory allocation. +This chapter shows the most common patterns. + +See also slides from talk: +[Sawicki, Adam. Advanced Graphics Techniques Tutorial: Memory management in Vulkan and DX12. Game Developers Conference, 2018](https://www.gdcvault.com/play/1025458/Advanced-Graphics-Techniques-Tutorial-New) + + +\section usage_patterns_gpu_only GPU-only resource + +When: +Any resources that you frequently write and read on GPU, +e.g. images used as color attachments (aka "render targets"), depth-stencil attachments, +images/buffers used as storage image/buffer (aka "Unordered Access View (UAV)"). + +What to do: +Let the library select the optimal memory type, which will likely have `VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT`. + +\code +VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; +imgCreateInfo.imageType = VK_IMAGE_TYPE_2D; +imgCreateInfo.extent.width = 3840; +imgCreateInfo.extent.height = 2160; +imgCreateInfo.extent.depth = 1; +imgCreateInfo.mipLevels = 1; +imgCreateInfo.arrayLayers = 1; +imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM; +imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; +imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; +imgCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; +imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; +allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; +allocCreateInfo.priority = 1.0f; + +VkImage img; +VmaAllocation alloc; +vmaCreateImage(allocator, &imgCreateInfo, &allocCreateInfo, &img, &alloc, nullptr); +\endcode + +Also consider: +Consider creating them as dedicated allocations using #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT, +especially if they are large or if you plan to destroy and recreate them with different sizes +e.g. when display resolution changes. +Prefer to create such resources first and all other GPU resources (like textures and vertex buffers) later. +When VK_EXT_memory_priority extension is enabled, it is also worth setting high priority to such allocation +to decrease chances to be evicted to system memory by the operating system. + +\section usage_patterns_staging_copy_upload Staging copy for upload + +When: +A "staging" buffer than you want to map and fill from CPU code, then use as a source of transfer +to some GPU resource. + +What to do: +Use flag #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT. +Let the library select the optimal memory type, which will always have `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT`. + +\code +VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufCreateInfo.size = 65536; +bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; +allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | + VMA_ALLOCATION_CREATE_MAPPED_BIT; + +VkBuffer buf; +VmaAllocation alloc; +VmaAllocationInfo allocInfo; +vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); + +... + +memcpy(allocInfo.pMappedData, myData, myDataSize); +\endcode + +Also consider: +You can map the allocation using vmaMapMemory() or you can create it as persistenly mapped +using #VMA_ALLOCATION_CREATE_MAPPED_BIT, as in the example above. + + +\section usage_patterns_readback Readback + +When: +Buffers for data written by or transferred from the GPU that you want to read back on the CPU, +e.g. results of some computations. + +What to do: +Use flag #VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT. +Let the library select the optimal memory type, which will always have `VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT` +and `VK_MEMORY_PROPERTY_HOST_CACHED_BIT`. + +\code +VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufCreateInfo.size = 65536; +bufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; +allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT | + VMA_ALLOCATION_CREATE_MAPPED_BIT; + +VkBuffer buf; +VmaAllocation alloc; +VmaAllocationInfo allocInfo; +vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); + +... + +const float* downloadedData = (const float*)allocInfo.pMappedData; +\endcode + + +\section usage_patterns_advanced_data_uploading Advanced data uploading + +For resources that you frequently write on CPU via mapped pointer and +frequently read on GPU e.g. as a uniform buffer (also called "dynamic"), multiple options are possible: + +-# Easiest solution is to have one copy of the resource in `HOST_VISIBLE` memory, + even if it means system RAM (not `DEVICE_LOCAL`) on systems with a discrete graphics card, + and make the device reach out to that resource directly. + - Reads performed by the device will then go through PCI Express bus. + The performance of this access may be limited, but it may be fine depending on the size + of this resource (whether it is small enough to quickly end up in GPU cache) and the sparsity + of access. +-# On systems with unified memory (e.g. AMD APU or Intel integrated graphics, mobile chips), + a memory type may be available that is both `HOST_VISIBLE` (available for mapping) and `DEVICE_LOCAL` + (fast to access from the GPU). Then, it is likely the best choice for such type of resource. +-# Systems with a discrete graphics card and separate video memory may or may not expose + a memory type that is both `HOST_VISIBLE` and `DEVICE_LOCAL`, also known as Base Address Register (BAR). + If they do, it represents a piece of VRAM (or entire VRAM, if ReBAR is enabled in the motherboard BIOS) + that is available to CPU for mapping. + - Writes performed by the host to that memory go through PCI Express bus. + The performance of these writes may be limited, but it may be fine, especially on PCIe 4.0, + as long as rules of using uncached and write-combined memory are followed - only sequential writes and no reads. +-# Finally, you may need or prefer to create a separate copy of the resource in `DEVICE_LOCAL` memory, + a separate "staging" copy in `HOST_VISIBLE` memory and perform an explicit transfer command between them. + +Thankfully, VMA offers an aid to create and use such resources in the the way optimal +for the current Vulkan device. To help the library make the best choice, +use flag #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT together with +#VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT. +It will then prefer a memory type that is both `DEVICE_LOCAL` and `HOST_VISIBLE` (integrated memory or BAR), +but if no such memory type is available or allocation from it fails +(PC graphics cards have only 256 MB of BAR by default, unless ReBAR is supported and enabled in BIOS), +it will fall back to `DEVICE_LOCAL` memory for fast GPU access. +It is then up to you to detect that the allocation ended up in a memory type that is not `HOST_VISIBLE`, +so you need to create another "staging" allocation and perform explicit transfers. + +\code +VkBufferCreateInfo bufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; +bufCreateInfo.size = 65536; +bufCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; +allocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | + VMA_ALLOCATION_CREATE_HOST_ACCESS_ALLOW_TRANSFER_INSTEAD_BIT | + VMA_ALLOCATION_CREATE_MAPPED_BIT; + +VkBuffer buf; +VmaAllocation alloc; +VmaAllocationInfo allocInfo; +vmaCreateBuffer(allocator, &bufCreateInfo, &allocCreateInfo, &buf, &alloc, &allocInfo); + +VkMemoryPropertyFlags memPropFlags; +vmaGetAllocationMemoryProperties(allocator, alloc, &memPropFlags); + +if(memPropFlags & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) +{ + // Allocation ended up in a mappable memory and is already mapped - write to it directly. + + // [Executed in runtime]: + memcpy(allocInfo.pMappedData, myData, myDataSize); +} +else +{ + // Allocation ended up in a non-mappable memory - need to transfer. + VkBufferCreateInfo stagingBufCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; + stagingBufCreateInfo.size = 65536; + stagingBufCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + + VmaAllocationCreateInfo stagingAllocCreateInfo = {}; + stagingAllocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; + stagingAllocCreateInfo.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | + VMA_ALLOCATION_CREATE_MAPPED_BIT; + + VkBuffer stagingBuf; + VmaAllocation stagingAlloc; + VmaAllocationInfo stagingAllocInfo; + vmaCreateBuffer(allocator, &stagingBufCreateInfo, &stagingAllocCreateInfo, + &stagingBuf, &stagingAlloc, stagingAllocInfo); + + // [Executed in runtime]: + memcpy(stagingAllocInfo.pMappedData, myData, myDataSize); + //vkCmdPipelineBarrier: VK_ACCESS_HOST_WRITE_BIT --> VK_ACCESS_TRANSFER_READ_BIT + VkBufferCopy bufCopy = { + 0, // srcOffset + 0, // dstOffset, + myDataSize); // size + vkCmdCopyBuffer(cmdBuf, stagingBuf, buf, 1, &bufCopy); +} +\endcode + +\section usage_patterns_other_use_cases Other use cases + +Here are some other, less obvious use cases and their recommended settings: + +- An image that is used only as transfer source and destination, but it should stay on the device, + as it is used to temporarily store a copy of some texture, e.g. from the current to the next frame, + for temporal antialiasing or other temporal effects. + - Use `VkImageCreateInfo::usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT` + - Use VmaAllocationCreateInfo::usage = #VMA_MEMORY_USAGE_AUTO +- An image that is used only as transfer source and destination, but it should be placed + in the system RAM despite it doesn't need to be mapped, because it serves as a "swap" copy to evict + least recently used textures from VRAM. + - Use `VkImageCreateInfo::usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT` + - Use VmaAllocationCreateInfo::usage = #VMA_MEMORY_USAGE_AUTO_PREFER_HOST, + as VMA needs a hint here to differentiate from the previous case. +- A buffer that you want to map and write from the CPU, directly read from the GPU + (e.g. as a uniform or vertex buffer), but you have a clear preference to place it in device or + host memory due to its large size. + - Use `VkBufferCreateInfo::usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT` + - Use VmaAllocationCreateInfo::usage = #VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE or #VMA_MEMORY_USAGE_AUTO_PREFER_HOST + - Use VmaAllocationCreateInfo::flags = #VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT + + +\page configuration Configuration + +Please check "CONFIGURATION SECTION" in the code to find macros that you can define +before each include of this file or change directly in this file to provide +your own implementation of basic facilities like assert, `min()` and `max()` functions, +mutex, atomic etc. +The library uses its own implementation of containers by default, but you can switch to using +STL containers instead. + +For example, define `VMA_ASSERT(expr)` before including the library to provide +custom implementation of the assertion, compatible with your project. +By default it is defined to standard C `assert(expr)` in `_DEBUG` configuration +and empty otherwise. + +\section config_Vulkan_functions Pointers to Vulkan functions + +There are multiple ways to import pointers to Vulkan functions in the library. +In the simplest case you don't need to do anything. +If the compilation or linking of your program or the initialization of the #VmaAllocator +doesn't work for you, you can try to reconfigure it. + +First, the allocator tries to fetch pointers to Vulkan functions linked statically, +like this: + +\code +m_VulkanFunctions.vkAllocateMemory = (PFN_vkAllocateMemory)vkAllocateMemory; +\endcode + +If you want to disable this feature, set configuration macro: `#define VMA_STATIC_VULKAN_FUNCTIONS 0`. + +Second, you can provide the pointers yourself by setting member VmaAllocatorCreateInfo::pVulkanFunctions. +You can fetch them e.g. using functions `vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` or +by using a helper library like [volk](https://github.com/zeux/volk). + +Third, VMA tries to fetch remaining pointers that are still null by calling +`vkGetInstanceProcAddr` and `vkGetDeviceProcAddr` on its own. +You need to only fill in VmaVulkanFunctions::vkGetInstanceProcAddr and VmaVulkanFunctions::vkGetDeviceProcAddr. +Other pointers will be fetched automatically. +If you want to disable this feature, set configuration macro: `#define VMA_DYNAMIC_VULKAN_FUNCTIONS 0`. + +Finally, all the function pointers required by the library (considering selected +Vulkan version and enabled extensions) are checked with `VMA_ASSERT` if they are not null. + + +\section custom_memory_allocator Custom host memory allocator + +If you use custom allocator for CPU memory rather than default operator `new` +and `delete` from C++, you can make this library using your allocator as well +by filling optional member VmaAllocatorCreateInfo::pAllocationCallbacks. These +functions will be passed to Vulkan, as well as used by the library itself to +make any CPU-side allocations. + +\section allocation_callbacks Device memory allocation callbacks + +The library makes calls to `vkAllocateMemory()` and `vkFreeMemory()` internally. +You can setup callbacks to be informed about these calls, e.g. for the purpose +of gathering some statistics. To do it, fill optional member +VmaAllocatorCreateInfo::pDeviceMemoryCallbacks. + +\section heap_memory_limit Device heap memory limit + +When device memory of certain heap runs out of free space, new allocations may +fail (returning error code) or they may succeed, silently pushing some existing_ +memory blocks from GPU VRAM to system RAM (which degrades performance). This +behavior is implementation-dependent - it depends on GPU vendor and graphics +driver. + +On AMD cards it can be controlled while creating Vulkan device object by using +VK_AMD_memory_overallocation_behavior extension, if available. + +Alternatively, if you want to test how your program behaves with limited amount of Vulkan device +memory available without switching your graphics card to one that really has +smaller VRAM, you can use a feature of this library intended for this purpose. +To do it, fill optional member VmaAllocatorCreateInfo::pHeapSizeLimit. + + + +\page vk_khr_dedicated_allocation VK_KHR_dedicated_allocation + +VK_KHR_dedicated_allocation is a Vulkan extension which can be used to improve +performance on some GPUs. It augments Vulkan API with possibility to query +driver whether it prefers particular buffer or image to have its own, dedicated +allocation (separate `VkDeviceMemory` block) for better efficiency - to be able +to do some internal optimizations. The extension is supported by this library. +It will be used automatically when enabled. + +It has been promoted to core Vulkan 1.1, so if you use eligible Vulkan version +and inform VMA about it by setting VmaAllocatorCreateInfo::vulkanApiVersion, +you are all set. + +Otherwise, if you want to use it as an extension: + +1 . When creating Vulkan device, check if following 2 device extensions are +supported (call `vkEnumerateDeviceExtensionProperties()`). +If yes, enable them (fill `VkDeviceCreateInfo::ppEnabledExtensionNames`). + +- VK_KHR_get_memory_requirements2 +- VK_KHR_dedicated_allocation + +If you enabled these extensions: + +2 . Use #VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT flag when creating +your #VmaAllocator to inform the library that you enabled required extensions +and you want the library to use them. + +\code +allocatorInfo.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT; + +vmaCreateAllocator(&allocatorInfo, &allocator); +\endcode + +That is all. The extension will be automatically used whenever you create a +buffer using vmaCreateBuffer() or image using vmaCreateImage(). + +When using the extension together with Vulkan Validation Layer, you will receive +warnings like this: + +_vkBindBufferMemory(): Binding memory to buffer 0x33 but vkGetBufferMemoryRequirements() has not been called on that buffer._ + +It is OK, you should just ignore it. It happens because you use function +`vkGetBufferMemoryRequirements2KHR()` instead of standard +`vkGetBufferMemoryRequirements()`, while the validation layer seems to be +unaware of it. + +To learn more about this extension, see: + +- [VK_KHR_dedicated_allocation in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap50.html#VK_KHR_dedicated_allocation) +- [VK_KHR_dedicated_allocation unofficial manual](http://asawicki.info/articles/VK_KHR_dedicated_allocation.php5) + + + +\page vk_ext_memory_priority VK_EXT_memory_priority + +VK_EXT_memory_priority is a device extension that allows to pass additional "priority" +value to Vulkan memory allocations that the implementation may use prefer certain +buffers and images that are critical for performance to stay in device-local memory +in cases when the memory is over-subscribed, while some others may be moved to the system memory. + +VMA offers convenient usage of this extension. +If you enable it, you can pass "priority" parameter when creating allocations or custom pools +and the library automatically passes the value to Vulkan using this extension. + +If you want to use this extension in connection with VMA, follow these steps: + +\section vk_ext_memory_priority_initialization Initialization + +1) Call `vkEnumerateDeviceExtensionProperties` for the physical device. +Check if the extension is supported - if returned array of `VkExtensionProperties` contains "VK_EXT_memory_priority". + +2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`. +Attach additional structure `VkPhysicalDeviceMemoryPriorityFeaturesEXT` to `VkPhysicalDeviceFeatures2::pNext` to be returned. +Check if the device feature is really supported - check if `VkPhysicalDeviceMemoryPriorityFeaturesEXT::memoryPriority` is true. + +3) While creating device with `vkCreateDevice`, enable this extension - add "VK_EXT_memory_priority" +to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`. + +4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`. +Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`. +Enable this device feature - attach additional structure `VkPhysicalDeviceMemoryPriorityFeaturesEXT` to +`VkPhysicalDeviceFeatures2::pNext` chain and set its member `memoryPriority` to `VK_TRUE`. + +5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you +have enabled this extension and feature - add #VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT +to VmaAllocatorCreateInfo::flags. + +\section vk_ext_memory_priority_usage Usage + +When using this extension, you should initialize following member: + +- VmaAllocationCreateInfo::priority when creating a dedicated allocation with #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. +- VmaPoolCreateInfo::priority when creating a custom pool. + +It should be a floating-point value between `0.0f` and `1.0f`, where recommended default is `0.5f`. +Memory allocated with higher value can be treated by the Vulkan implementation as higher priority +and so it can have lower chances of being pushed out to system memory, experiencing degraded performance. + +It might be a good idea to create performance-critical resources like color-attachment or depth-stencil images +as dedicated and set high priority to them. For example: + +\code +VkImageCreateInfo imgCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; +imgCreateInfo.imageType = VK_IMAGE_TYPE_2D; +imgCreateInfo.extent.width = 3840; +imgCreateInfo.extent.height = 2160; +imgCreateInfo.extent.depth = 1; +imgCreateInfo.mipLevels = 1; +imgCreateInfo.arrayLayers = 1; +imgCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM; +imgCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; +imgCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; +imgCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; +imgCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + +VmaAllocationCreateInfo allocCreateInfo = {}; +allocCreateInfo.usage = VMA_MEMORY_USAGE_AUTO; +allocCreateInfo.flags = VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT; +allocCreateInfo.priority = 1.0f; + +VkImage img; +VmaAllocation alloc; +vmaCreateImage(allocator, &imgCreateInfo, &allocCreateInfo, &img, &alloc, nullptr); +\endcode + +`priority` member is ignored in the following situations: + +- Allocations created in custom pools: They inherit the priority, along with all other allocation parameters + from the parametrs passed in #VmaPoolCreateInfo when the pool was created. +- Allocations created in default pools: They inherit the priority from the parameters + VMA used when creating default pools, which means `priority == 0.5f`. + + +\page vk_amd_device_coherent_memory VK_AMD_device_coherent_memory + +VK_AMD_device_coherent_memory is a device extension that enables access to +additional memory types with `VK_MEMORY_PROPERTY_DEVICE_COHERENT_BIT_AMD` and +`VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` flag. It is useful mostly for +allocation of buffers intended for writing "breadcrumb markers" in between passes +or draw calls, which in turn are useful for debugging GPU crash/hang/TDR cases. + +When the extension is available but has not been enabled, Vulkan physical device +still exposes those memory types, but their usage is forbidden. VMA automatically +takes care of that - it returns `VK_ERROR_FEATURE_NOT_PRESENT` when an attempt +to allocate memory of such type is made. + +If you want to use this extension in connection with VMA, follow these steps: + +\section vk_amd_device_coherent_memory_initialization Initialization + +1) Call `vkEnumerateDeviceExtensionProperties` for the physical device. +Check if the extension is supported - if returned array of `VkExtensionProperties` contains "VK_AMD_device_coherent_memory". + +2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`. +Attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to `VkPhysicalDeviceFeatures2::pNext` to be returned. +Check if the device feature is really supported - check if `VkPhysicalDeviceCoherentMemoryFeaturesAMD::deviceCoherentMemory` is true. + +3) While creating device with `vkCreateDevice`, enable this extension - add "VK_AMD_device_coherent_memory" +to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`. + +4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`. +Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`. +Enable this device feature - attach additional structure `VkPhysicalDeviceCoherentMemoryFeaturesAMD` to +`VkPhysicalDeviceFeatures2::pNext` and set its member `deviceCoherentMemory` to `VK_TRUE`. + +5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you +have enabled this extension and feature - add #VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT +to VmaAllocatorCreateInfo::flags. + +\section vk_amd_device_coherent_memory_usage Usage + +After following steps described above, you can create VMA allocations and custom pools +out of the special `DEVICE_COHERENT` and `DEVICE_UNCACHED` memory types on eligible +devices. There are multiple ways to do it, for example: + +- You can request or prefer to allocate out of such memory types by adding + `VK_MEMORY_PROPERTY_DEVICE_UNCACHED_BIT_AMD` to VmaAllocationCreateInfo::requiredFlags + or VmaAllocationCreateInfo::preferredFlags. Those flags can be freely mixed with + other ways of \ref choosing_memory_type, like setting VmaAllocationCreateInfo::usage. +- If you manually found memory type index to use for this purpose, force allocation + from this specific index by setting VmaAllocationCreateInfo::memoryTypeBits `= 1u << index`. + +\section vk_amd_device_coherent_memory_more_information More information + +To learn more about this extension, see [VK_AMD_device_coherent_memory in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_AMD_device_coherent_memory.html) + +Example use of this extension can be found in the code of the sample and test suite +accompanying this library. + + +\page enabling_buffer_device_address Enabling buffer device address + +Device extension VK_KHR_buffer_device_address +allow to fetch raw GPU pointer to a buffer and pass it for usage in a shader code. +It has been promoted to core Vulkan 1.2. + +If you want to use this feature in connection with VMA, follow these steps: + +\section enabling_buffer_device_address_initialization Initialization + +1) (For Vulkan version < 1.2) Call `vkEnumerateDeviceExtensionProperties` for the physical device. +Check if the extension is supported - if returned array of `VkExtensionProperties` contains +"VK_KHR_buffer_device_address". + +2) Call `vkGetPhysicalDeviceFeatures2` for the physical device instead of old `vkGetPhysicalDeviceFeatures`. +Attach additional structure `VkPhysicalDeviceBufferDeviceAddressFeatures*` to `VkPhysicalDeviceFeatures2::pNext` to be returned. +Check if the device feature is really supported - check if `VkPhysicalDeviceBufferDeviceAddressFeatures::bufferDeviceAddress` is true. + +3) (For Vulkan version < 1.2) While creating device with `vkCreateDevice`, enable this extension - add +"VK_KHR_buffer_device_address" to the list passed as `VkDeviceCreateInfo::ppEnabledExtensionNames`. + +4) While creating the device, also don't set `VkDeviceCreateInfo::pEnabledFeatures`. +Fill in `VkPhysicalDeviceFeatures2` structure instead and pass it as `VkDeviceCreateInfo::pNext`. +Enable this device feature - attach additional structure `VkPhysicalDeviceBufferDeviceAddressFeatures*` to +`VkPhysicalDeviceFeatures2::pNext` and set its member `bufferDeviceAddress` to `VK_TRUE`. + +5) While creating #VmaAllocator with vmaCreateAllocator() inform VMA that you +have enabled this feature - add #VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT +to VmaAllocatorCreateInfo::flags. + +\section enabling_buffer_device_address_usage Usage + +After following steps described above, you can create buffers with `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT*` using VMA. +The library automatically adds `VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT*` to +allocated memory blocks wherever it might be needed. + +Please note that the library supports only `VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT*`. +The second part of this functionality related to "capture and replay" is not supported, +as it is intended for usage in debugging tools like RenderDoc, not in everyday Vulkan usage. + +\section enabling_buffer_device_address_more_information More information + +To learn more about this extension, see [VK_KHR_buffer_device_address in Vulkan specification](https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/chap46.html#VK_KHR_buffer_device_address) + +Example use of this extension can be found in the code of the sample and test suite +accompanying this library. + +\page general_considerations General considerations + +\section general_considerations_thread_safety Thread safety + +- The library has no global state, so separate #VmaAllocator objects can be used + independently. + There should be no need to create multiple such objects though - one per `VkDevice` is enough. +- By default, all calls to functions that take #VmaAllocator as first parameter + are safe to call from multiple threads simultaneously because they are + synchronized internally when needed. + This includes allocation and deallocation from default memory pool, as well as custom #VmaPool. +- When the allocator is created with #VMA_ALLOCATOR_CREATE_EXTERNALLY_SYNCHRONIZED_BIT + flag, calls to functions that take such #VmaAllocator object must be + synchronized externally. +- Access to a #VmaAllocation object must be externally synchronized. For example, + you must not call vmaGetAllocationInfo() and vmaMapMemory() from different + threads at the same time if you pass the same #VmaAllocation object to these + functions. +- #VmaVirtualBlock is not safe to be used from multiple threads simultaneously. + +\section general_considerations_versioning_and_compatibility Versioning and compatibility + +The library uses [**Semantic Versioning**](https://semver.org/), +which means version numbers follow convention: Major.Minor.Patch (e.g. 2.3.0), where: + +- Incremented Patch version means a release is backward- and forward-compatible, + introducing only some internal improvements, bug fixes, optimizations etc. + or changes that are out of scope of the official API described in this documentation. +- Incremented Minor version means a release is backward-compatible, + so existing code that uses the library should continue to work, while some new + symbols could have been added: new structures, functions, new values in existing + enums and bit flags, new structure members, but not new function parameters. +- Incrementing Major version means a release could break some backward compatibility. + +All changes between official releases are documented in file "CHANGELOG.md". + +\warning Backward compatibility is considered on the level of C++ source code, not binary linkage. +Adding new members to existing structures is treated as backward compatible if initializing +the new members to binary zero results in the old behavior. +You should always fully initialize all library structures to zeros and not rely on their +exact binary size. + +\section general_considerations_validation_layer_warnings Validation layer warnings + +When using this library, you can meet following types of warnings issued by +Vulkan validation layer. They don't necessarily indicate a bug, so you may need +to just ignore them. + +- *vkBindBufferMemory(): Binding memory to buffer 0xeb8e4 but vkGetBufferMemoryRequirements() has not been called on that buffer.* + - It happens when VK_KHR_dedicated_allocation extension is enabled. + `vkGetBufferMemoryRequirements2KHR` function is used instead, while validation layer seems to be unaware of it. +- *Mapping an image with layout VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL can result in undefined behavior if this memory is used by the device. Only GENERAL or PREINITIALIZED should be used.* + - It happens when you map a buffer or image, because the library maps entire + `VkDeviceMemory` block, where different types of images and buffers may end + up together, especially on GPUs with unified memory like Intel. +- *Non-linear image 0xebc91 is aliased with linear buffer 0xeb8e4 which may indicate a bug.* + - It may happen when you use [defragmentation](@ref defragmentation). + +\section general_considerations_allocation_algorithm Allocation algorithm + +The library uses following algorithm for allocation, in order: + +-# Try to find free range of memory in existing blocks. +-# If failed, try to create a new block of `VkDeviceMemory`, with preferred block size. +-# If failed, try to create such block with size / 2, size / 4, size / 8. +-# If failed, try to allocate separate `VkDeviceMemory` for this allocation, + just like when you use #VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT. +-# If failed, choose other memory type that meets the requirements specified in + VmaAllocationCreateInfo and go to point 1. +-# If failed, return `VK_ERROR_OUT_OF_DEVICE_MEMORY`. + +\section general_considerations_features_not_supported Features not supported + +Features deliberately excluded from the scope of this library: + +-# **Data transfer.** Uploading (streaming) and downloading data of buffers and images + between CPU and GPU memory and related synchronization is responsibility of the user. + Defining some "texture" object that would automatically stream its data from a + staging copy in CPU memory to GPU memory would rather be a feature of another, + higher-level library implemented on top of VMA. + VMA doesn't record any commands to a `VkCommandBuffer`. It just allocates memory. +-# **Recreation of buffers and images.** Although the library has functions for + buffer and image creation: vmaCreateBuffer(), vmaCreateImage(), you need to + recreate these objects yourself after defragmentation. That is because the big + structures `VkBufferCreateInfo`, `VkImageCreateInfo` are not stored in + #VmaAllocation object. +-# **Handling CPU memory allocation failures.** When dynamically creating small C++ + objects in CPU memory (not Vulkan memory), allocation failures are not checked + and handled gracefully, because that would complicate code significantly and + is usually not needed in desktop PC applications anyway. + Success of an allocation is just checked with an assert. +-# **Code free of any compiler warnings.** Maintaining the library to compile and + work correctly on so many different platforms is hard enough. Being free of + any warnings, on any version of any compiler, is simply not feasible. + There are many preprocessor macros that make some variables unused, function parameters unreferenced, + or conditional expressions constant in some configurations. + The code of this library should not be bigger or more complicated just to silence these warnings. + It is recommended to disable such warnings instead. +-# This is a C++ library with C interface. **Bindings or ports to any other programming languages** are welcome as external projects but + are not going to be included into this repository. +*/ diff --git a/src/CRE/GL/uniform_buffer.hpp b/src/CRE/GL/uniform_buffer.hpp new file mode 100644 index 00000000..ff0ca664 --- /dev/null +++ b/src/CRE/GL/uniform_buffer.hpp @@ -0,0 +1,136 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#pragma once +#include +#include "../gl_state.hpp" + +namespace GL { + struct UniformBuffer { + private: + GLuint buffer; + GLsizeiptr size; + + public: + inline UniformBuffer() : buffer(), size() { + + } + + inline ~UniformBuffer() { + + } + + inline void Create(size_t size) { + if (buffer) + return; + + this->size = (GLsizeiptr)size; + + if (GLAD_GL_VERSION_4_5) { + glCreateBuffers(1, &buffer); + glNamedBufferStorage(buffer, + this->size, 0, GL_DYNAMIC_STORAGE_BIT | GL_MAP_WRITE_BIT); + } + else { + glGenBuffers(1, &buffer); + gl_state_bind_uniform_buffer(buffer); + if (GLAD_GL_VERSION_4_4) + glBufferStorage(GL_UNIFORM_BUFFER, (GLsizeiptr)size, + 0, GL_DYNAMIC_STORAGE_BIT | GL_MAP_WRITE_BIT); + else + glBufferData(GL_UNIFORM_BUFFER, (GLsizeiptr)size, 0, GL_DYNAMIC_DRAW); + } + + } + + inline void Create(size_t size, const void* data) { + if (buffer) + return; + + this->size = (GLsizeiptr)size; + + if (GLAD_GL_VERSION_4_5) { + glCreateBuffers(1, &buffer); + glNamedBufferStorage(buffer, (GLsizeiptr)size, data, 0); + } + else { + glGenBuffers(1, &buffer); + gl_state_bind_uniform_buffer(buffer); + if (GLAD_GL_VERSION_4_4) + glBufferStorage(GL_UNIFORM_BUFFER, (GLsizeiptr)size, data, 0); + else + glBufferData(GL_UNIFORM_BUFFER, (GLsizeiptr)size, data, GL_STATIC_DRAW); + } + } + + inline void Bind(int32_t index) { + gl_state_bind_uniform_buffer_range(index, buffer, 0, size); + } + + inline void Destroy() { + if (buffer) { + glDeleteBuffers(1, &buffer); + buffer = 0; + } + } + + inline void* MapMemory() { + if (!buffer) + return 0; + + void* data; + if (GLAD_GL_VERSION_4_5) + data = glMapNamedBuffer(buffer, GL_WRITE_ONLY); + else { + gl_state_bind_array_buffer(buffer); + data = glMapBuffer(GL_UNIFORM_BUFFER, GL_WRITE_ONLY); + } + + if (data) + return data; + + if (!GLAD_GL_VERSION_4_5) + gl_state_bind_array_buffer(0); + return 0; + } + + inline void UnmapMemory() { + if (!buffer) + return; + + if (GLAD_GL_VERSION_4_5) + glUnmapNamedBuffer(buffer); + else { + glUnmapBuffer(GL_UNIFORM_BUFFER); + gl_state_bind_array_buffer(0); + } + } + + inline void WriteMapMemory(size_t offset, size_t size, const void* data) { + if (!buffer) + return; + + if (GLAD_GL_VERSION_4_5) + glNamedBufferSubData(buffer, (GLsizeiptr)offset, (GLsizeiptr)size, data); + else { + gl_state_bind_uniform_buffer(buffer); + glBufferSubData(GL_UNIFORM_BUFFER, (GLsizeiptr)offset, (GLsizeiptr)size, data); + } + } + + template + inline void WriteMapMemory(T& data) { + if (!buffer) + return; + + if (GLAD_GL_VERSION_4_5) + glNamedBufferSubData(buffer, 0, sizeof(T), &data); + else { + gl_state_bind_uniform_buffer(buffer); + glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(T), &data); + } + } + }; +} diff --git a/src/CRE/Glitter/curve.cpp b/src/CRE/Glitter/curve.cpp index 5500cb39..4ecf2135 100644 --- a/src/CRE/Glitter/curve.cpp +++ b/src/CRE/Glitter/curve.cpp @@ -18,6 +18,23 @@ namespace Glitter { } + Curve::Key::Key(KeyType type, int32_t frame, float_t value, float_t random_range) : tangent1(), tangent2() { + this->type = type; + this->frame = frame; + this->value = value; + this->random_range = random_range; + } + + Curve::Key::Key(KeyType type, int32_t frame, float_t value, + float_t tangent1, float_t tangent2, float_t random_range) { + this->type = type; + this->frame = frame; + this->value = value; + this->tangent1 = tangent1; + this->tangent2 = tangent2; + this->random_range = random_range; + } + Curve::Curve(GLT) : type(), repeat(), start_time(), end_time(), flags(), random_range() { version = GLT_VAL == Glitter::X ? 0x02 : 0x01; @@ -265,13 +282,7 @@ namespace Glitter { if (left_count < reverse_min_count) { Curve::Key key; if (left_count == 1) { - key.type = KEY_CONSTANT; - key.frame = frame; - key.value = a[0]; - key.tangent1 = t2_old; - key.tangent2 = 0.0f; - key.random_range = b[0]; - keys_rev.push_back(key); + keys_rev.push_back(Curve::Key(KEY_CONSTANT, frame, a[0], t2_old, 0.0f, b[0])); t2_old = 0.0f; } else { @@ -396,14 +407,8 @@ namespace Glitter { left_count -= c; } - Curve::Key key; - key.type = KEY_CONSTANT; - key.frame = (int32_t)(start_time + (count - 1) * step); - key.value = arr_a[count - 1]; - key.tangent1 = t2_old; - key.tangent2 = 0.0f; - key.random_range = arr_b[count - 1]; - keys_rev.push_back(key); + keys_rev.push_back(Curve::Key(KEY_CONSTANT, (int32_t)(start_time + (count - 1) * step), + arr_a[count - 1], t2_old, 0.0f, arr_b[count - 1])); free_def(arr_a); free_def(arr_b); @@ -436,41 +441,33 @@ namespace Glitter { Curve::Key first_key = keys_rev.data()[0]; Curve::Key last_key = keys_rev.data()[keys_count - 1]; - Curve::Key key; keys.reserve(count); for (size_t i = 0; i < count; i++) { int32_t frame = start_time + (int32_t)(i * step); if (frame <= first_key.frame) { - key.type = KEY_CONSTANT; - key.frame = frame; - key.value = first_key.value; - key.random_range = first_key.random_range; - keys.push_back(key); + keys.push_back(Curve::Key(KEY_CONSTANT, frame, first_key.value, first_key.random_range)); continue; } else if (frame >= last_key.frame) { - key.type = KEY_CONSTANT; - key.frame = frame; - key.value = last_key.value; - key.random_range = last_key.random_range; - keys.push_back(key); + keys.push_back(Curve::Key(KEY_CONSTANT, frame, last_key.value, last_key.random_range)); continue; } - size_t key_idx = 0; + Curve::Key* key = keys_rev.data(); + size_t length = keys_count; size_t temp; while (length > 0) - if (frame > keys_rev.data()[key_idx + (temp = length >> 1)].frame) { - key_idx += temp + 1; + if (frame < key[temp = length / 2].frame) + length = temp; + else { + key += temp + 1; length -= temp + 1; } - else - length = temp; - Curve::Key* curr_key = &keys_rev.data()[key_idx - 1]; - Curve::Key* next_key = &keys_rev.data()[key_idx]; + Curve::Key* curr_key = key - 1; + Curve::Key* next_key = key; float_t val; float_t rand_range; @@ -497,11 +494,7 @@ namespace Glitter { (float_t)curr_key->frame, (float_t)next_key->frame, (float_t)frame); } - key.type = KEY_CONSTANT; - key.frame = frame; - key.value = val; - key.random_range = rand_range; - keys.push_back(key); + keys.push_back(Curve::Key(KEY_CONSTANT, frame, val, 0.0f, 0.0f, rand_range)); } } #endif @@ -639,48 +632,20 @@ namespace Glitter { bool has_error_lerp, bool has_error_hermite, float_t* a, float_t* b, int32_t frame, const uint8_t step, size_t i, float_t t1, float_t t2, float_t t2_old, std::vector* keys_rev) { - Curve::Key key; if (has_error && has_error_lerp && has_error_hermite) { - float_t _t2 = t2_old; - for (size_t j = 0; j < i; j++) { - key.type = KEY_CONSTANT; - key.frame = (int32_t)(frame + j * step); - key.value = a[j]; - key.tangent1 = _t2; - key.tangent2 = 0.0f; - key.random_range = b[j]; - keys_rev->push_back(key); - _t2 = 0.0f; - } + keys_rev->reserve(i); + keys_rev->push_back(Curve::Key(KEY_CONSTANT, frame, a[0], t2_old, 0.0f, b[0])); + for (size_t j = 1; j < i; j++) + keys_rev->push_back(Curve::Key(KEY_CONSTANT, (int32_t)(frame + j * step), a[j], b[j])); } else if (has_error && has_error_lerp) { - key.type = KEY_HERMITE; - key.frame = frame; - key.value = a[0]; - key.tangent1 = t2_old; - key.tangent2 = t1; - key.random_range = b[0]; - keys_rev->push_back(key); + keys_rev->push_back(Curve::Key(KEY_HERMITE, frame, a[0], t2_old, t1, b[0])); return t2; } - else if (has_error || (i > 1 && b[0] != b[1])) { - key.type = KEY_LINEAR; - key.frame = frame; - key.value = a[0]; - key.tangent1 = 0.0f; - key.tangent2 = 0.0f; - key.random_range = b[0]; - keys_rev->push_back(key); - } - else { - key.type = KEY_CONSTANT; - key.frame = frame; - key.value = a[0]; - key.tangent1 = 0.0f; - key.tangent2 = 0.0f; - key.random_range = b[0]; - keys_rev->push_back(key); - } + else if (has_error || (i > 1 && b[0] != b[1])) + keys_rev->push_back(Curve::Key(KEY_LINEAR, frame, a[0], b[0])); + else + keys_rev->push_back(Curve::Key(KEY_CONSTANT, frame, a[0], b[0])); return 0.0f; } #endif diff --git a/src/CRE/Glitter/effect_group.cpp b/src/CRE/Glitter/effect_group.cpp index 89e8760c..cae6ed5d 100644 --- a/src/CRE/Glitter/effect_group.cpp +++ b/src/CRE/Glitter/effect_group.cpp @@ -4,6 +4,7 @@ */ #include "glitter.hpp" +#include "../../KKdLib/prj/algorithm.hpp" #include "../object.hpp" #include "../texture.hpp" @@ -88,19 +89,14 @@ namespace Glitter { continue; has_model = true; - uint32_t object_set_hash = (uint32_t)k->data.mesh.object_set_name_hash; - bool found = false; - for (uint32_t& l : object_set_ids) - if (l == object_set_hash) { - found = true; - break; - } - - if (!found) - object_set_ids.push_back(object_set_hash); + object_set_ids.push_back((uint32_t)k->data.mesh.object_set_name_hash); } } } + + prj::sort(object_set_ids); + prj::unique(object_set_ids); + return has_model; } diff --git a/src/CRE/Glitter/effect_inst.cpp b/src/CRE/Glitter/effect_inst.cpp index da1279aa..313a81e0 100644 --- a/src/CRE/Glitter/effect_inst.cpp +++ b/src/CRE/Glitter/effect_inst.cpp @@ -67,10 +67,8 @@ namespace Glitter { return -1; bone_database* bone_data = (bone_database*)GPM_VAL->bone_data; - std::vector* motion_bone_names = 0; - if (!bone_data || bone_data->skeleton.size() < 1 - || !bone_data->get_skeleton_motion_bones( - bone_database_skeleton_type_to_string(BONE_DATABASE_SKELETON_COMMON), &motion_bone_names)) + const std::vector* motion_bone_names = 0; + if (!bone_data || bone_data->skeleton.size() < 1) return -1; static const char* bone_names[] = { @@ -94,11 +92,8 @@ namespace Glitter { "kl_toe_r_wj" }; - uint64_t bone_name_hash = hash_utf8_fnv1a64m(bone_names[node]); - for (std::string& i : *motion_bone_names) - if (bone_name_hash == hash_string_fnv1a64m(i)) - return (int32_t)(&i - motion_bone_names->data()); - return -1; + return bone_data->get_skeleton_motion_bone_index( + bone_database_skeleton_type_to_string(BONE_DATABASE_SKELETON_COMMON), bone_names[node]); } F2EffectInst::ExtAnim::ExtAnim() { @@ -610,7 +605,7 @@ namespace Glitter { } XEffectInst::XEffectInst(GPM, Effect* eff, - size_t id, float_t emission, bool appear_now) + size_t id, float_t emission, bool appear_now, uint8_t load_flags) : EffectInst(GPM_VAL, Glitter::X, eff, id, emission, appear_now) { mat_rot = mat4_identity; mat_rot_eff_rot = mat4_identity; @@ -618,6 +613,11 @@ namespace Glitter { ext_anim = 0; render_scene = {}; + GPM_VAL->CounterIncrement(); + + if (load_flags & 0x01) + enum_or(flags, EFFECT_INST_FLAG_23); + if (data.flags & EFFECT_USE_SEED) random = data.seed; else diff --git a/src/CRE/Glitter/glitter.hpp b/src/CRE/Glitter/glitter.hpp index 0d4ffa3d..48c945bd 100644 --- a/src/CRE/Glitter/glitter.hpp +++ b/src/CRE/Glitter/glitter.hpp @@ -16,6 +16,7 @@ #include "../../KKdLib/mat.hpp" #include "../../KKdLib/txp.hpp" #include "../../KKdLib/vec.hpp" +#include "../GL/uniform_buffer.hpp" #include "../file_handler.hpp" #include "../frame_rate_control.hpp" #include "../static_var.hpp" @@ -423,8 +424,9 @@ namespace Glitter { class F2RenderScene; class XRenderScene; + struct BatchShaderData; + class Scene; class GltParticleManager; - struct Scene; #define GPM Glitter::GltParticleManager* glt_particle_manager #define GPM_VAL (glt_particle_manager) @@ -487,7 +489,7 @@ namespace Glitter { struct Buffer { vec3 position; - vec2 uv; + vec2 uv[2]; vec4 color; }; @@ -512,6 +514,9 @@ namespace Glitter { float_t random_range; Key(); + Key(KeyType type, int32_t frame, float_t value, float_t random_range); + Key(KeyType type, int32_t frame, float_t value, + float_t tangent1, float_t tangent2, float_t random_range); }; CurveType type; @@ -713,8 +718,8 @@ namespace Glitter { size_t count; size_t ctrl; size_t disp; - int32_t texture; - int32_t mask_texture; + GLuint texture; + GLuint mask_texture; float_t frame; mat4 mat; mat4 mat_rot; @@ -929,7 +934,7 @@ namespace Glitter { ExtAnim* ext_anim; XRenderScene render_scene; - XEffectInst(GPM, Effect* eff, size_t id, float_t emission, bool appear_now); + XEffectInst(GPM, Effect* eff, size_t id, float_t emission, bool appear_now, uint8_t load_flags = 0); virtual ~XEffectInst() override; virtual void Copy(EffectInst* dst, float_t emission) override; @@ -1246,8 +1251,8 @@ namespace Glitter { float_t emission; uint64_t tex_hash; uint64_t mask_tex_hash; - int32_t texture; - int32_t mask_texture; + GLuint texture; + GLuint mask_texture; int32_t unk0; int32_t unk1; Particle::Mesh mesh; @@ -1463,7 +1468,15 @@ namespace Glitter { operator uint32_t() const { return (counter << 8) || index; } }; - struct Scene { + struct BatchShaderData { + vec4 g_mvp[4]; + vec4 g_glitter_blend_color; + vec4 g_state_material_diffuse; + vec4 g_state_material_emission; + }; + + class Scene { + public: #if defined(CRE_DEV) || defined(ReDIVA_DEV) std::string name; #endif @@ -1500,7 +1513,7 @@ namespace Glitter { bool FreeEffectByID(GPM, size_t id, bool free); bool HasEnded(bool a2); bool HasEnded(size_t id, bool a3); - void InitEffect(GPM, Effect* eff, size_t id, bool appear_now); + void InitEffect(GPM, Effect* eff, size_t id, bool appear_now, uint8_t load_flags = 0); bool ResetEffect(GPM, uint64_t effect_hash, size_t* id = 0); bool SetExtColor(bool set, uint64_t effect_hash, float_t r, float_t g, float_t b, float_t a); bool SetExtColorByID(bool set, size_t id, float_t r, float_t g, float_t b, float_t a); @@ -1532,6 +1545,7 @@ namespace Glitter { bool draw_all; bool draw_all_mesh; bool draw_selected; + GL::UniformBuffer batch_ubo; GltParticleManager(); virtual ~GltParticleManager() override; @@ -1580,8 +1594,9 @@ namespace Glitter { uint64_t LoadFile(GLT, void* data, const char* file, const char* path, float_t emission, bool init_scene, object_database* obj_db); SceneCounter LoadScene(uint64_t effect_group_hash, uint64_t effect_hash, bool appear_now = true); - SceneCounter LoadSceneEffect(uint64_t hash, bool appear_now = true); - SceneCounter LoadSceneEffect(uint64_t hash, const char* name, bool appear_now = true); + SceneCounter LoadSceneEffect(uint64_t hash, bool appear_now = true, uint8_t load_flags = 0); + SceneCounter LoadSceneEffect(uint64_t hash, const char* name, + bool appear_now = true, uint8_t load_flags = 0); bool SceneHasNotEnded(SceneCounter load_counter); #if defined(CRE_DEV) || defined(ReDIVA_DEV) void SetFrame(EffectGroup* effect_group, @@ -1606,7 +1621,7 @@ namespace Glitter { extern void axis_angle_from_vectors(vec3* axis, float_t* angle, const vec3* vec0, const vec3* vec1); extern void glt_particle_manager_init(); - extern bool glt_particle_manager_append_task(); - extern bool glt_particle_manager_free_task(); + extern bool glt_particle_manager_add_task(); + extern bool glt_particle_manager_del_task(); extern void glt_particle_manager_free(); } diff --git a/src/CRE/Glitter/particle_manager.cpp b/src/CRE/Glitter/particle_manager.cpp index 1d03ddc9..c127c2a0 100644 --- a/src/CRE/Glitter/particle_manager.cpp +++ b/src/CRE/Glitter/particle_manager.cpp @@ -24,10 +24,12 @@ namespace Glitter { draw_all_mesh = true; scenes.reserve(0x100); file_readers.reserve(0x100); + batch_ubo.Create(sizeof(BatchShaderData)); } GltParticleManager::~GltParticleManager() { FreeEffects(); + batch_ubo.Destroy(); } bool GltParticleManager::Init() { @@ -291,6 +293,11 @@ namespace Glitter { #endif i->Disp(this, disp_type); } + + gl_state_bind_vertex_array(0); + gl_state_disable_blend(); + gl_state_enable_cull_face(); + gl_state_disable_depth_test(); } void GltParticleManager::FreeEffects() { @@ -703,11 +710,11 @@ namespace Glitter { return counter; } - SceneCounter GltParticleManager::LoadSceneEffect(uint64_t hash, bool appear_now) { + SceneCounter GltParticleManager::LoadSceneEffect(uint64_t hash, bool appear_now, uint8_t load_flags) { if (hash == hash_fnv1a64m_empty || hash == hash_murmurhash_empty) return 0; - if (scenes.size()) + if (!(load_flags & 0x02) && scenes.size()) for (Scene*& i : scenes) if (i && i->ResetEffect(this, hash)) { CheckSceneHasLocalEffect(i); @@ -747,26 +754,27 @@ namespace Glitter { if (v8->not_loaded) return 0; - for (Scene*& j : scenes) - if (j && j->hash == i.first) { - size_t id = 1; - for (Effect*& k : v8->effects) { - if (!j) - continue; + if (!(load_flags & 0x02)) + for (Scene*& j : scenes) + if (j && j->hash == i.first) { + size_t id = 1; + for (Effect*& k : v8->effects) { + if (!j) + continue; - if (k->data.name_hash == hash) { - j->InitEffect(this, k, id, appear_now); - break; + if (k->data.name_hash == hash) { + j->InitEffect(this, k, id, appear_now, load_flags); + break; + } + id++; } - id++; - } - CheckSceneHasLocalEffect(j); + CheckSceneHasLocalEffect(j); - SceneCounter counter = j->counter; - if (j->type == Glitter::X) - counter.index = (uint8_t)id; - return counter; - } + SceneCounter counter = j->counter; + if (j->type == Glitter::X) + counter.index = (uint8_t)id; + return counter; + } SceneCounter counter = GetSceneCounter(); if (!counter) @@ -797,15 +805,16 @@ namespace Glitter { return 0; } - SceneCounter GltParticleManager::LoadSceneEffect(uint64_t hash, const char* name, bool appear_now) { - SceneCounter counter = LoadSceneEffect(hash, appear_now); + SceneCounter GltParticleManager::LoadSceneEffect(uint64_t hash, + const char* name, bool appear_now, uint8_t load_flags) { + SceneCounter counter = LoadSceneEffect(hash, appear_now, load_flags); for (Scene*& i : scenes) { if (!i || i->counter.counter != counter.counter) continue; for (SceneEffect& j : i->effects) if (j.ptr && j.disp && j.ptr->id == counter.index) { - j.ptr->name = name; + j.ptr->name.assign(name); break; } break; @@ -970,12 +979,12 @@ namespace Glitter { glt_particle_manager = new GltParticleManager; } - bool glt_particle_manager_append_task() { - return app::TaskWork::AppendTask(glt_particle_manager, "GLITTER_TASK", 2); + bool glt_particle_manager_add_task() { + return app::TaskWork::AddTask(glt_particle_manager, "GLITTER_TASK", 2); } - bool glt_particle_manager_free_task() { - return glt_particle_manager->SetDest(); + bool glt_particle_manager_del_task() { + return glt_particle_manager->DelTask(); } void glt_particle_manager_free() { diff --git a/src/CRE/Glitter/render_group.cpp b/src/CRE/Glitter/render_group.cpp index d66cc706..df1f9d6b 100644 --- a/src/CRE/Glitter/render_group.cpp +++ b/src/CRE/Glitter/render_group.cpp @@ -119,16 +119,16 @@ namespace Glitter { glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, buffer_size, - (void*)offsetof(Buffer, position)); // Pos + (void*)offsetof(Buffer, position)); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, buffer_size, + (void*)offsetof(Buffer, uv[0])); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, buffer_size, + (void*)offsetof(Buffer, uv[1])); glEnableVertexAttribArray(3); glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, buffer_size, - (void*)offsetof(Buffer, color)); // Color0 - glEnableVertexAttribArray(8); - glVertexAttribPointer(8, 2, GL_FLOAT, GL_FALSE, buffer_size, - (void*)offsetof(Buffer, uv)); // TexCoord0 - glEnableVertexAttribArray(9); - glVertexAttribPointer(9, 2, GL_FLOAT, GL_FALSE, buffer_size, - (void*)offsetof(Buffer, uv)); // TexCoord1 + (void*)offsetof(Buffer, color)); gl_state_bind_array_buffer(0); gl_state_bind_vertex_array(0); @@ -465,16 +465,16 @@ namespace Glitter { glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, buffer_size, - (void*)offsetof(Buffer, position)); // Pos + (void*)offsetof(Buffer, position)); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, buffer_size, + (void*)offsetof(Buffer, uv[0])); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, buffer_size, + (void*)offsetof(Buffer, uv[1])); glEnableVertexAttribArray(3); glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, buffer_size, - (void*)offsetof(Buffer, color)); // Color0 - glEnableVertexAttribArray(8); - glVertexAttribPointer(8, 2, GL_FLOAT, GL_FALSE, buffer_size, - (void*)offsetof(Buffer, uv)); // TexCoord0 - glEnableVertexAttribArray(9); - glVertexAttribPointer(9, 2, GL_FLOAT, GL_FALSE, buffer_size, - (void*)offsetof(Buffer, uv)); // TexCoord1 + (void*)offsetof(Buffer, color)); gl_state_bind_array_buffer(0); gl_state_bind_vertex_array(0); diff --git a/src/CRE/Glitter/render_scene.cpp b/src/CRE/Glitter/render_scene.cpp index 342e96c1..67142bab 100644 --- a/src/CRE/Glitter/render_scene.cpp +++ b/src/CRE/Glitter/render_scene.cpp @@ -4,7 +4,6 @@ */ #include "glitter.hpp" -#include "../draw_task.hpp" #include "../gl_state.hpp" #include "../render_context.hpp" #include "../shader.hpp" @@ -219,7 +218,8 @@ namespace Glitter { for (LocusHistory::Data& hist_data : hist->data) { vec3& pos = hist_data.translation; buf->position = pos + (pos - elem->base_translation) * scale; - buf->uv = 0.0f; + buf->uv[0] = 0.0f; + buf->uv[1] = 0.0f; buf->color = hist_data.color; j++; buf++; @@ -227,7 +227,8 @@ namespace Glitter { else for (LocusHistory::Data& hist_data : hist->data) { buf->position = hist_data.translation; - buf->uv = 0.0f; + buf->uv[0] = 0.0f; + buf->uv[1] = 0.0f; buf->color = hist_data.color; j++; buf++; @@ -354,13 +355,15 @@ namespace Glitter { v00, v01); buf[0].position = pos + x_vec * v00; - buf[0].uv.x = uv_u; - buf[0].uv.y = uv_v_2nd + (float_t)j * uv_v_scale; + buf[0].uv[0].x = uv_u; + buf[0].uv[0].y = uv_v_2nd + (float_t)j * uv_v_scale; + buf[0].uv[1] = buf[0].uv[0]; buf[0].color = hist_data.color; buf[1].position = pos + x_vec * v01; - buf[1].uv.x = uv_u_2nd; - buf[1].uv.y = uv_v_2nd + (float_t)j * uv_v_scale; + buf[1].uv[0].x = uv_u_2nd; + buf[1].uv[0].y = uv_v_2nd + (float_t)j * uv_v_scale; + buf[1].uv[1] = buf[1].uv[0]; buf[1].color = hist_data.color; j++; buf += 2; @@ -376,13 +379,15 @@ namespace Glitter { v00, v01); buf[0].position = pos + x_vec * v00; - buf[0].uv.x = uv_u; - buf[0].uv.y = uv_v_2nd + (float_t)j * uv_v_scale; + buf[0].uv[0].x = uv_u; + buf[0].uv[0].y = uv_v_2nd + (float_t)j * uv_v_scale; + buf[0].uv[1] = buf[0].uv[0]; buf[0].color = hist_data.color; buf[1].position = pos + x_vec * v01; - buf[1].uv.x = uv_u_2nd; - buf[1].uv.y = uv_v_2nd + (float_t)j * uv_v_scale; + buf[1].uv[0].x = uv_u_2nd; + buf[1].uv[0].y = uv_v_2nd + (float_t)j * uv_v_scale; + buf[1].uv[1] = buf[1].uv[0]; buf[1].color = hist_data.color; j++; buf += 2; @@ -589,7 +594,8 @@ namespace Glitter { vec3 x_vec_rot = x_vec * (pos_add[k].x * rot_z_cos - pos_add[k].y * rot_z_sin); vec3 y_vec_rot = y_vec * (pos_add[k].x * rot_z_sin + pos_add[k].y * rot_z_cos); buf->position = pos + (x_vec_rot + y_vec_rot); - buf->uv = uv + uv_add[k]; + buf->uv[0] = uv + uv_add[k]; + buf->uv[1] = buf->uv[0]; buf->color = elem->color; } disp++; @@ -753,7 +759,8 @@ namespace Glitter { xy_vec_rot.z = 0.0f; mat3_mult_vec(&ptc_rot, &xy_vec_rot, &xy_vec_rot); buf->position = pos + xy_vec_rot; - buf->uv = uv + uv_add[k]; + buf->uv[0] = uv + uv_add[k]; + buf->uv[1] = buf->uv[0]; buf->color = elem->color; } disp++; @@ -825,7 +832,8 @@ namespace Glitter { vec3 x_vec_rot = x_vec * (rot_z_cos * pos_add[k].x - rot_z_sin * pos_add[k].y); vec3 y_vec_rot = y_vec * (rot_z_sin * pos_add[k].x + rot_z_cos * pos_add[k].y); buf->position = pos + (x_vec_rot + y_vec_rot); - buf->uv = uv + uv_add[k]; + buf->uv[0] = uv + uv_add[k]; + buf->uv[1] = buf->uv[0]; buf->color = elem->color; } disp++; @@ -944,24 +952,48 @@ namespace Glitter { if (rend_group->disp < 1) return; - gl_state_enable_blend(); + mat4 mat; + mat4_mult(&rend_group->mat_draw, &GPM_VAL->cam.view, &mat); + mat4_mult(&mat, &GPM_VAL->cam.projection, &mat); + + float_t emission = 1.0f; + if (rend_group->flags & PARTICLE_EMISSION || rend_group->blend_mode == PARTICLE_BLEND_TYPICAL) + emission = rend_group->emission; + + BatchShaderData shader_data = {}; + mat4_transpose(&mat, &mat); + shader_data.g_mvp[0] = mat.row0; + shader_data.g_mvp[1] = mat.row1; + shader_data.g_mvp[2] = mat.row2; + shader_data.g_mvp[3] = mat.row3; + shader_data.g_glitter_blend_color = 1.0f; + shader_data.g_state_material_diffuse = 0.0f; + shader_data.g_state_material_emission = { emission, emission, emission, 1.0f }; + GPM_VAL->batch_ubo.WriteMapMemory(shader_data); + + GLenum blend_src = GL_SRC_ALPHA; + GLenum blend_dst = GL_ONE_MINUS_SRC_ALPHA; switch (rend_group->blend_mode) { case PARTICLE_BLEND_ADD: - gl_state_set_blend_func(GL_SRC_ALPHA, GL_ONE); + blend_src = GL_SRC_ALPHA; + blend_dst = GL_ONE; break; case PARTICLE_BLEND_MULTIPLY: - gl_state_set_blend_func(GL_ZERO, GL_SRC_COLOR); - break; - default: - gl_state_set_blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + blend_src = GL_ZERO; + blend_dst = GL_SRC_COLOR; break; } + + gl_state_enable_blend(); + gl_state_set_blend_func(blend_src, blend_dst); gl_state_set_blend_equation(GL_FUNC_ADD); + GLuint texture = 0; + GLuint mask_texture = 0; if (rend_group->type != PARTICLE_LINE && rend_group->texture) { - gl_state_active_bind_texture_2d(0, rend_group->texture); + texture = rend_group->texture; if (rend_group->mask_texture) { - gl_state_active_bind_texture_2d(1, rend_group->mask_texture); + mask_texture = rend_group->mask_texture; uniform_value[U_TEXTURE_COUNT] = 2; switch (rend_group->mask_blend_mode) { @@ -977,18 +1009,18 @@ namespace Glitter { } } else { - gl_state_active_bind_texture_2d(1, 0); uniform_value[U_TEXTURE_COUNT] = 1; uniform_value[U_TEXTURE_BLEND] = 0; } } else { - gl_state_active_bind_texture_2d(0, 0); - gl_state_active_bind_texture_2d(1, 0); uniform_value[U_TEXTURE_COUNT] = 0; uniform_value[U_TEXTURE_BLEND] = 0; } + gl_state_active_bind_texture_2d(0, texture); + gl_state_active_bind_texture_2d(1, mask_texture); + switch (rend_group->type) { case PARTICLE_QUAD: switch (rend_group->fog_type) { @@ -1040,40 +1072,26 @@ namespace Glitter { break; } - float_t emission = 1.0f; - if (rend_group->flags & PARTICLE_EMISSION - || rend_group->blend_mode == PARTICLE_BLEND_TYPICAL) - emission = rend_group->emission; - - shaders_ft.state_material_set_emission(false, emission, emission, emission, 1.0f); - shaders_ft.state_matrix_set_mvp(rend_group->mat_draw, GPM_VAL->cam.view, GPM_VAL->cam.projection); - shaders_ft.state_matrix_set_texture(0, mat4_identity); - shaders_ft.state_matrix_set_texture(1, mat4_identity); - shaders_ft.env_vert_set(3, 1.0f); - - shaders_ft.set(SHADER_FT_GLITTER_PT); + shaders_ft.set_opengl_shader(SHADER_FT_GLITTER_PT); + GPM_VAL->batch_ubo.Bind(2); switch (rend_group->type) { - case PARTICLE_QUAD: { + case PARTICLE_QUAD: gl_state_bind_vertex_array(rend_group->vao); shaders_ft.enable_primitive_restart(); - shaders_ft.set_primitive_restart_index(0xFFFFFFFF); shaders_ft.draw_elements(GL_TRIANGLE_STRIP, (GLsizei)(5 * rend_group->disp - 1), GL_UNSIGNED_INT, 0); shaders_ft.disable_primitive_restart(); - gl_state_bind_vertex_array(0); - - } break; + break; case PARTICLE_LINE: - case PARTICLE_LOCUS: { + case PARTICLE_LOCUS: gl_state_bind_vertex_array(rend_group->vao); - const GLenum mode = rend_group->type == PARTICLE_LINE ? GL_LINE_STRIP : GL_TRIANGLE_STRIP; - for (std::pair& i : rend_group->draw_list) - shaders_ft.draw_arrays(mode, i.first, i.second); - - } break; + if (rend_group->type == PARTICLE_LINE) + for (std::pair& i : rend_group->draw_list) + shaders_ft.draw_arrays(GL_LINE_STRIP, i.first, i.second); + else + for (std::pair& i : rend_group->draw_list) + shaders_ft.draw_arrays(GL_TRIANGLE_STRIP, i.first, i.second); + break; } - gl_state_disable_blend(); - gl_state_enable_cull_face(); - gl_state_disable_depth_test(); } XRenderScene::XRenderScene() { @@ -1213,7 +1231,8 @@ namespace Glitter { for (LocusHistory::Data& hist_data : hist->data) { vec3& pos = hist_data.translation; buf->position = pos + (pos - elem->base_translation) * scale; - buf->uv = 0.0f; + buf->uv[0] = 0.0f; + buf->uv[1] = 0.0f; buf->color = hist_data.color; j++; buf++; @@ -1221,7 +1240,8 @@ namespace Glitter { else for (LocusHistory::Data& hist_data : hist->data) { buf->position = hist_data.translation; - buf->uv = 0.0f; + buf->uv[0] = 0.0f; + buf->uv[1] = 0.0f; buf->color = hist_data.color; j++; buf++; @@ -1346,13 +1366,15 @@ namespace Glitter { v00, v01); buf[0].position = pos + x_vec * v00; - buf[0].uv.x = uv_u; - buf[0].uv.y = uv_v_2nd + (float_t)j * uv_v_scale; + buf[0].uv[0].x = uv_u; + buf[0].uv[0].y = uv_v_2nd + (float_t)j * uv_v_scale; + buf[0].uv[1] = buf[0].uv[0]; buf[0].color = hist_data.color; buf[1].position = pos + x_vec * v01; - buf[1].uv.x = uv_u_2nd; - buf[1].uv.y = uv_v_2nd + (float_t)j * uv_v_scale; + buf[1].uv[0].x = uv_u_2nd; + buf[1].uv[0].y = uv_v_2nd + (float_t)j * uv_v_scale; + buf[1].uv[1] = buf[1].uv[0]; buf[1].color = hist_data.color; j++; buf += 2; @@ -1368,13 +1390,15 @@ namespace Glitter { v00, v01); buf[0].position = pos + x_vec * v00; - buf[0].uv.x = uv_u; - buf[0].uv.y = uv_v_2nd + (float_t)j * uv_v_scale; + buf[0].uv[0].x = uv_u; + buf[0].uv[0].y = uv_v_2nd + (float_t)j * uv_v_scale; + buf[0].uv[1] = buf[0].uv[0]; buf[0].color = hist_data.color; buf[1].position = pos + x_vec * v01; - buf[1].uv.x = uv_u_2nd; - buf[1].uv.y = uv_v_2nd + (float_t)j * uv_v_scale; + buf[1].uv[0].x = uv_u_2nd; + buf[1].uv[0].y = uv_v_2nd + (float_t)j * uv_v_scale; + buf[1].uv[1] = buf[1].uv[0]; buf[1].color = hist_data.color; j++; buf += 2; @@ -1514,8 +1538,8 @@ namespace Glitter { emit_scale += ext_anim_scale; } - object_data* object_data = &rctx_ptr->object_data; - object_data->set_texture_pattern(0, 0); + mdl::DispManager& disp_manager = rctx_ptr->disp_manager; + disp_manager.set_texture_pattern(0, 0); RenderElement* elem = rend_group->elements; size_t disp = 0; @@ -1570,20 +1594,20 @@ namespace Glitter { mat4_set_translation(&tex_trans[0].mat, &uv_scroll); mat4_set_translation(&tex_trans[1].mat, &uv_scroll_2nd); - object_data->set_texture_transform(tex_trans_count, tex_trans); + disp_manager.set_texture_transform(tex_trans_count, tex_trans); if (local) mat4_mult(&mat, &GPM_VAL->cam.inv_view, &mat); - if (draw_task_add_draw_object_by_object_info(rctx_ptr, + if (disp_manager.entry_obj_by_object_info( &mat, object_info, &elem->color, 0, local)) disp++; elem->mat_draw = mat; - object_data->set_texture_transform(0, 0); + disp_manager.set_texture_transform(0, 0); } } - object_data->set_texture_pattern(0, 0); + disp_manager.set_texture_pattern(0, 0); rend_group->disp = disp; } @@ -1767,7 +1791,8 @@ namespace Glitter { vec3 x_vec_rot = x_vec * (pos_add[k].x * rot_z_cos - pos_add[k].y * rot_z_sin); vec3 y_vec_rot = y_vec * (pos_add[k].x * rot_z_sin + pos_add[k].y * rot_z_cos); buf->position = pos + (x_vec_rot + y_vec_rot); - buf->uv = uv + uv_add[k]; + buf->uv[0] = uv + uv_add[k]; + buf->uv[1] = buf->uv[0]; buf->color = elem->color; } disp++; @@ -1925,7 +1950,8 @@ namespace Glitter { xy_vec_rot.z = 0.0f; mat3_mult_vec(&ptc_rot, &xy_vec_rot, &xy_vec_rot); buf->position = pos + xy_vec_rot; - buf->uv = uv + uv_add[k]; + buf->uv[0] = uv + uv_add[k]; + buf->uv[1] = buf->uv[0]; buf->color = elem->color; } disp++; @@ -1992,7 +2018,8 @@ namespace Glitter { vec3 x_vec_rot = x_vec * (rot_z_cos * pos_add[k].x - rot_z_sin * pos_add[k].y); vec3 y_vec_rot = y_vec * (rot_z_sin * pos_add[k].x + rot_z_cos * pos_add[k].y); buf->position = pos + (x_vec_rot + y_vec_rot); - buf->uv = uv + uv_add[k]; + buf->uv[0] = uv + uv_add[k]; + buf->uv[1] = buf->uv[0]; buf->color = elem->color; } disp++; @@ -2119,24 +2146,48 @@ namespace Glitter { if (rend_group->disp < 1) return; - gl_state_enable_blend(); + mat4 mat; + mat4_mult(&rend_group->mat_draw, &GPM_VAL->cam.view, &mat); + mat4_mult(&mat, &GPM_VAL->cam.projection, &mat); + + float_t emission = 1.0f; + if (rend_group->flags & PARTICLE_EMISSION || rend_group->blend_mode == PARTICLE_BLEND_TYPICAL) + emission = rend_group->emission; + + BatchShaderData shader_data = {}; + mat4_transpose(&mat, &mat); + shader_data.g_mvp[0] = mat.row0; + shader_data.g_mvp[1] = mat.row1; + shader_data.g_mvp[2] = mat.row2; + shader_data.g_mvp[3] = mat.row3; + shader_data.g_glitter_blend_color = 1.0f; + shader_data.g_state_material_diffuse = 0.0f; + shader_data.g_state_material_emission = { emission, emission, emission, 1.0f }; + GPM_VAL->batch_ubo.WriteMapMemory(shader_data); + + GLenum blend_src = GL_SRC_ALPHA; + GLenum blend_dst = GL_ONE_MINUS_SRC_ALPHA; switch (rend_group->blend_mode) { case PARTICLE_BLEND_ADD: - gl_state_set_blend_func(GL_SRC_ALPHA, GL_ONE); + blend_src = GL_SRC_ALPHA; + blend_dst = GL_ONE; break; case PARTICLE_BLEND_MULTIPLY: - gl_state_set_blend_func(GL_ZERO, GL_SRC_COLOR); - break; - default: - gl_state_set_blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + blend_src = GL_ZERO; + blend_dst = GL_SRC_COLOR; break; } + + gl_state_enable_blend(); + gl_state_set_blend_func(blend_src, blend_dst); gl_state_set_blend_equation(GL_FUNC_ADD); + GLuint texture = 0; + GLuint mask_texture = 0; if (rend_group->type != PARTICLE_LINE && rend_group->texture) { - gl_state_active_bind_texture_2d(0, rend_group->texture); + texture = rend_group->texture; if (rend_group->mask_texture) { - gl_state_active_bind_texture_2d(1, rend_group->mask_texture); + mask_texture = rend_group->mask_texture; uniform_value[U_TEXTURE_COUNT] = 2; switch (rend_group->mask_blend_mode) { @@ -2152,18 +2203,18 @@ namespace Glitter { } } else { - gl_state_active_bind_texture_2d(1, 0); uniform_value[U_TEXTURE_COUNT] = 1; uniform_value[U_TEXTURE_BLEND] = 0; } } else { - gl_state_active_bind_texture_2d(0, 0); - gl_state_active_bind_texture_2d(1, 0); uniform_value[U_TEXTURE_COUNT] = 0; uniform_value[U_TEXTURE_BLEND] = 0; } + gl_state_active_bind_texture_2d(0, texture); + gl_state_active_bind_texture_2d(1, mask_texture); + switch (rend_group->fog_type) { default: uniform_value[U_FOG_HEIGHT] = 0; @@ -2198,37 +2249,25 @@ namespace Glitter { else gl_state_disable_cull_face(); - float_t emission = 1.0f; - if (rend_group->flags & PARTICLE_EMISSION || rend_group->blend_mode == PARTICLE_BLEND_TYPICAL) - emission = rend_group->emission; - - shaders_ft.state_material_set_emission(false, emission, emission, emission, 1.0f); - shaders_ft.state_matrix_set_mvp(rend_group->mat_draw, GPM_VAL->cam.view, GPM_VAL->cam.projection); - shaders_ft.state_matrix_set_texture(0, mat4_identity); - shaders_ft.state_matrix_set_texture(1, mat4_identity); - shaders_ft.env_vert_set(3, 1.0f); - - shaders_ft.set(SHADER_FT_GLITTER_PT); + shaders_ft.set_opengl_shader(SHADER_FT_GLITTER_PT); + GPM_VAL->batch_ubo.Bind(2); switch (rend_group->type) { - case PARTICLE_QUAD: { + case PARTICLE_QUAD: gl_state_bind_vertex_array(rend_group->vao); shaders_ft.enable_primitive_restart(); - shaders_ft.set_primitive_restart_index(0xFFFFFFFF); shaders_ft.draw_elements(GL_TRIANGLE_STRIP, (GLsizei)(5 * rend_group->disp - 1), GL_UNSIGNED_INT, 0); shaders_ft.disable_primitive_restart(); - gl_state_bind_vertex_array(0); - } break; + break; case PARTICLE_LINE: - case PARTICLE_LOCUS: { + case PARTICLE_LOCUS: gl_state_bind_vertex_array(rend_group->vao); - const GLenum mode = rend_group->type == PARTICLE_LINE ? GL_LINE_STRIP : GL_TRIANGLE_STRIP; - for (std::pair& i : rend_group->draw_list) - shaders_ft.draw_arrays(mode, i.first, i.second); - gl_state_bind_vertex_array(0); - } break; + if (rend_group->type == PARTICLE_LINE) + for (std::pair& i : rend_group->draw_list) + shaders_ft.draw_arrays(GL_LINE_STRIP, i.first, i.second); + else + for (std::pair& i : rend_group->draw_list) + shaders_ft.draw_arrays(GL_TRIANGLE_STRIP, i.first, i.second); + break; } - gl_state_disable_blend(); - gl_state_enable_cull_face(); - gl_state_disable_depth_test(); } } diff --git a/src/CRE/Glitter/scene.cpp b/src/CRE/Glitter/scene.cpp index 9000bea2..a12c7fdd 100644 --- a/src/CRE/Glitter/scene.cpp +++ b/src/CRE/Glitter/scene.cpp @@ -267,7 +267,7 @@ namespace Glitter { return true; } - void Scene::InitEffect(GPM, Effect* eff, size_t id, bool appear_now) { + void Scene::InitEffect(GPM, Effect* eff, size_t id, bool appear_now, uint8_t load_flags) { if (!eff) return; @@ -281,7 +281,7 @@ namespace Glitter { if (type != Glitter::X) effect.ptr = new F2EffectInst(GPM_VAL, type, eff, id, emission, appear_now); else - effect.ptr = new XEffectInst(GPM_VAL, eff, id, emission, appear_now); + effect.ptr = new XEffectInst(GPM_VAL, eff, id, emission, appear_now, load_flags); effect.disp = true; effects.push_back(effect); diff --git a/src/CRE/Vulkan/buffer.cpp b/src/CRE/Vulkan/buffer.cpp new file mode 100644 index 00000000..77abd936 --- /dev/null +++ b/src/CRE/Vulkan/buffer.cpp @@ -0,0 +1,75 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#include "buffer.hpp" + +namespace Vulkan { + Buffer::Buffer() : allocator(), data(), allocation() { + + } + + Buffer::~Buffer() { + + } + + bool Buffer::Create(VmaAllocator allocator, VkDeviceSize size, + VkBufferUsageFlags usage, VmaMemoryUsage memory_usage, VmaAllocationCreateFlags flags) { + return Create(allocator, size, usage, VK_SHARING_MODE_EXCLUSIVE, memory_usage, flags); + } + + bool Buffer::Create(VmaAllocator allocator, VkDeviceSize size, + VkBufferUsageFlags usage, VkSharingMode sharing_mode, + VmaMemoryUsage memory_usage, VmaAllocationCreateFlags flags) { + if (allocation) + return true; + + VkBufferCreateInfo buffer_create_info = {}; + buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_create_info.size = size; + buffer_create_info.usage = usage; + buffer_create_info.sharingMode = sharing_mode; + + VmaAllocationCreateInfo allocation_create_info = {}; + allocation_create_info.usage = memory_usage; + allocation_create_info.flags = flags; + + if (vmaCreateBuffer(allocator, &buffer_create_info, + &allocation_create_info, &data, &allocation, 0) != VK_SUCCESS) { + this->allocator = 0; + data = 0; + allocation = 0; + return false; + } + + this->allocator = allocator; + return true; + } + + void Buffer::Destroy() { + if (allocation) + vmaDestroyBuffer(allocator, data, allocation); + + this->allocator = 0; + data = 0; + allocation = 0; + } + + void* Buffer::MapMemory() { + void* data; + vmaMapMemory(allocator, allocation, &data); + return data; + } + + void Buffer::UnmapMemory() { + vmaUnmapMemory(allocator, allocation); + } + + void Buffer::WriteMapMemory(const void* data, size_t offset, size_t size) { + void* _data; + vmaMapMemory(allocator, allocation, &_data); + memcpy((void*)((size_t)_data + offset), data, size); + vmaUnmapMemory(allocator, allocation); + } +} diff --git a/src/CRE/Vulkan/buffer.hpp b/src/CRE/Vulkan/buffer.hpp new file mode 100644 index 00000000..24b2f1c9 --- /dev/null +++ b/src/CRE/Vulkan/buffer.hpp @@ -0,0 +1,35 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#pragma once + +#include "../../KKdLib/default.hpp" +#include +#define VMA_VULKAN_VERSION 1000000 +#include + +namespace Vulkan { + class Buffer { + protected: + VmaAllocator allocator; + + public: + VkBuffer data; + VmaAllocation allocation; + + Buffer(); + ~Buffer(); + + bool Create(VmaAllocator allocator, VkDeviceSize size, + VkBufferUsageFlags usage, VmaMemoryUsage memory_usage, VmaAllocationCreateFlags flags = 0); + bool Create(VmaAllocator allocator, VkDeviceSize size, + VkBufferUsageFlags usage, VkSharingMode sharing_mode, + VmaMemoryUsage memory_usage, VmaAllocationCreateFlags flags = 0); + void Destroy(); + void* MapMemory(); + void UnmapMemory(); + void WriteMapMemory(const void* data, size_t offset, size_t size); + }; +} diff --git a/src/CRE/Vulkan/command_buffer.cpp b/src/CRE/Vulkan/command_buffer.cpp new file mode 100644 index 00000000..f584a524 --- /dev/null +++ b/src/CRE/Vulkan/command_buffer.cpp @@ -0,0 +1,47 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#include "command_buffer.hpp" + +namespace Vulkan { + CommandBuffer::CommandBuffer() : device(), command_pool(), data() { + + } + + CommandBuffer::~CommandBuffer() { + + } + + bool CommandBuffer::Allocate(VkDevice device, VkCommandPool command_pool, VkCommandBufferLevel level) { + if (data) + return true; + + VkCommandBufferAllocateInfo command_buffer_allocate_info = {}; + command_buffer_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + command_buffer_allocate_info.commandPool = command_pool; + command_buffer_allocate_info.level = level; + command_buffer_allocate_info.commandBufferCount = 1; + + if (vkAllocateCommandBuffers(device, &command_buffer_allocate_info, &data) != VK_SUCCESS) { + this->device = 0; + this->command_pool = 0; + data = 0; + return false; + } + + this->device = device; + this->command_pool = command_pool; + return true; + } + + void CommandBuffer::Free() { + if (data) + vkFreeCommandBuffers(device, command_pool, 1, &data); + + device = 0; + command_pool = 0; + data = 0; + } +} diff --git a/src/CRE/Vulkan/command_buffer.hpp b/src/CRE/Vulkan/command_buffer.hpp new file mode 100644 index 00000000..85963a7a --- /dev/null +++ b/src/CRE/Vulkan/command_buffer.hpp @@ -0,0 +1,27 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#pragma once + +#include "../../KKdLib/default.hpp" +#include + +namespace Vulkan { + class CommandBuffer { + protected: + VkDevice device; + VkCommandPool command_pool; + + public: + VkCommandBuffer data; + + CommandBuffer(); + ~CommandBuffer(); + + bool Allocate(VkDevice device, VkCommandPool command_pool, + VkCommandBufferLevel level = VK_COMMAND_BUFFER_LEVEL_PRIMARY); + void Free(); + }; +} diff --git a/src/CRE/Vulkan/fence.cpp b/src/CRE/Vulkan/fence.cpp new file mode 100644 index 00000000..874886dc --- /dev/null +++ b/src/CRE/Vulkan/fence.cpp @@ -0,0 +1,51 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#include "fence.hpp" + +namespace Vulkan { + Fence::Fence() : device(), data() { + + } + + Fence::~Fence() { + + } + + bool Fence::Create(VkDevice device, VkFenceCreateFlags flags) { + if (data) + return true; + + VkFenceCreateInfo fence_create_info; + fence_create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; + fence_create_info.pNext = 0; + fence_create_info.flags = flags; + + if (vkCreateFence(device, &fence_create_info, 0, &data) != VK_SUCCESS) { + this->device = 0; + data = 0; + return false; + } + + this->device = device; + return true; + } + + void Fence::Destroy() { + if (data) { + vkDestroyFence(device, data, 0); + data = 0; + } + device = 0; + } + + void Fence::Reset() { + vkResetFences(device, 1, &data); + } + + void Fence::WaitFor() { + vkWaitForFences(device, 1, &data, VK_TRUE, UINT64_MAX); + } +} diff --git a/src/CRE/Vulkan/fence.hpp b/src/CRE/Vulkan/fence.hpp new file mode 100644 index 00000000..b1766f7c --- /dev/null +++ b/src/CRE/Vulkan/fence.hpp @@ -0,0 +1,27 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#pragma once + +#include "../../KKdLib/default.hpp" +#include + +namespace Vulkan { + class Fence { + protected: + VkDevice device; + + public: + VkFence data; + + Fence(); + ~Fence(); + + bool Create(VkDevice device, VkFenceCreateFlags flags = 0); + void Destroy(); + void Reset(); + void WaitFor(); + }; +} diff --git a/src/CRE/Vulkan/framebuffer.cpp b/src/CRE/Vulkan/framebuffer.cpp new file mode 100644 index 00000000..9ddb5c19 --- /dev/null +++ b/src/CRE/Vulkan/framebuffer.cpp @@ -0,0 +1,51 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#include "framebuffer.hpp" + +namespace Vulkan { + Framebuffer::Framebuffer() : device(), data() { + + } + + Framebuffer::~Framebuffer() { + + } + + bool Framebuffer::Create(VkDevice device, VkFramebufferCreateFlags flags, + VkRenderPass render_pass, uint32_t attachment_count, const VkImageView* attachments, + uint32_t width, uint32_t height, uint32_t layers) { + if (data) + return true; + + VkFramebufferCreateInfo framebuffer_create_info; + framebuffer_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; + framebuffer_create_info.pNext = 0; + framebuffer_create_info.flags = 0; + framebuffer_create_info.renderPass = render_pass; + framebuffer_create_info.attachmentCount = attachment_count; + framebuffer_create_info.pAttachments = attachments; + framebuffer_create_info.width = width; + framebuffer_create_info.height = height; + framebuffer_create_info.layers = layers; + + if (vkCreateFramebuffer(device, &framebuffer_create_info, 0, &data) != VK_SUCCESS) { + this->device = 0; + data = 0; + return false; + } + + this->device = device; + return true; + } + + void Framebuffer::Destroy() { + if (data) { + vkDestroyFramebuffer(device, data, 0); + data = 0; + } + device = 0; + } +} diff --git a/src/CRE/Vulkan/framebuffer.hpp b/src/CRE/Vulkan/framebuffer.hpp new file mode 100644 index 00000000..c5c59546 --- /dev/null +++ b/src/CRE/Vulkan/framebuffer.hpp @@ -0,0 +1,27 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#pragma once + +#include "../../KKdLib/default.hpp" +#include + +namespace Vulkan { + class Framebuffer { + protected: + VkDevice device; + + public: + VkFramebuffer data; + + Framebuffer(); + ~Framebuffer(); + + bool Create(VkDevice device, VkFramebufferCreateFlags flags, + VkRenderPass render_pass, uint32_t attachment_count, const VkImageView* attachments, + uint32_t width, uint32_t height, uint32_t layers); + void Destroy(); + }; +} diff --git a/src/CRE/Vulkan/image.cpp b/src/CRE/Vulkan/image.cpp new file mode 100644 index 00000000..832be5f1 --- /dev/null +++ b/src/CRE/Vulkan/image.cpp @@ -0,0 +1,79 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#include "image.hpp" + +namespace Vulkan { + Image::Image() : allocator(), data(), allocation() { + + } + + Image::~Image() { + + } + + bool Image::Create(VmaAllocator allocator, + uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, + VkImageUsageFlags usage, VmaMemoryUsage memory_usage, VmaAllocationCreateFlags flags) { + if (allocation) + return true; + + VkImageCreateInfo image_create_info = {}; + image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; + image_create_info.imageType = VK_IMAGE_TYPE_2D; + image_create_info.extent.width = width; + image_create_info.extent.height = height; + image_create_info.extent.depth = 1; + image_create_info.mipLevels = 1; + image_create_info.arrayLayers = 1; + image_create_info.format = format; + image_create_info.tiling = tiling; + image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + image_create_info.usage = usage; + image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; + image_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VmaAllocationCreateInfo allocation_create_info = {}; + allocation_create_info.usage = memory_usage; + allocation_create_info.flags = flags; + + if (vmaCreateImage(allocator, &image_create_info, + &allocation_create_info, &data, &allocation, 0) != VK_SUCCESS) { + this->allocator = 0; + data = 0; + allocation = 0; + return false; + } + + this->allocator = allocator; + return true; + } + + void Image::Destroy() { + if (allocation) + vmaDestroyImage(allocator, data, allocation); + + this->allocator = 0; + data = 0; + allocation = 0; + } + + void* Image::MapMemory() { + void* data; + vmaMapMemory(allocator, allocation, &data); + return data; + } + + void Image::UnmapMemory() { + vmaUnmapMemory(allocator, allocation); + } + + void Image::WriteMapMemory(const void* data, size_t offset, size_t size) { + void* _data; + vmaMapMemory(allocator, allocation, &_data); + memcpy((void*)((size_t)_data + offset), data, size); + vmaUnmapMemory(allocator, allocation); + } +} diff --git a/src/CRE/Vulkan/image.hpp b/src/CRE/Vulkan/image.hpp new file mode 100644 index 00000000..615175d3 --- /dev/null +++ b/src/CRE/Vulkan/image.hpp @@ -0,0 +1,33 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#pragma once + +#include "../../KKdLib/default.hpp" +#include +#define VMA_VULKAN_VERSION 1000000 +#include + +namespace Vulkan { + class Image { + protected: + VmaAllocator allocator; + + public: + VkImage data; + VmaAllocation allocation; + + Image(); + ~Image(); + + bool Create(VmaAllocator allocator, + uint32_t width, uint32_t height, VkFormat format, VkImageTiling tiling, + VkImageUsageFlags usage, VmaMemoryUsage memory_usage, VmaAllocationCreateFlags flags = 0); + void Destroy(); + void* MapMemory(); + void UnmapMemory(); + void WriteMapMemory(const void* data, size_t offset, size_t size); + }; +} diff --git a/src/CRE/Vulkan/image_view.cpp b/src/CRE/Vulkan/image_view.cpp new file mode 100644 index 00000000..2d24659b --- /dev/null +++ b/src/CRE/Vulkan/image_view.cpp @@ -0,0 +1,75 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#include "image_view.hpp" + +namespace Vulkan { + ImageView::ImageView() : device(), data() { + + } + + ImageView::~ImageView() { + + } + + bool ImageView::Create(VkDevice device, VkImageViewCreateFlags flags, + VkImage image, VkImageViewType view_type, VkFormat format, + VkImageSubresourceRange& sub_resource_range) { + if (data) + return true; + + VkImageViewCreateInfo image_view_create_info; + image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + image_view_create_info.pNext = 0; + image_view_create_info.flags = flags; + image_view_create_info.image = image; + image_view_create_info.viewType = view_type; + image_view_create_info.format = format; + image_view_create_info.components = {}; + image_view_create_info.subresourceRange = sub_resource_range; + + if (vkCreateImageView(device, &image_view_create_info, 0, &data) != VK_SUCCESS) { + this->device = 0; + data = 0; + return false; + } + + this->device = device; + return true; + } + + bool ImageView::Create(VkDevice device, VkImageViewCreateFlags flags, + VkImage image, VkImageViewType view_type, VkFormat format, + VkComponentMapping& components, VkImageSubresourceRange& sub_resource_range) { + Destroy(); + + VkImageViewCreateInfo image_view_create_info; + image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; + image_view_create_info.pNext = 0; + image_view_create_info.flags = flags; + image_view_create_info.image = image; + image_view_create_info.viewType = view_type; + image_view_create_info.format = format; + image_view_create_info.components = components; + image_view_create_info.subresourceRange = sub_resource_range; + + if (vkCreateImageView(device, &image_view_create_info, 0, &data) != VK_SUCCESS) { + this->device = 0; + data = 0; + return false; + } + + this->device = device; + return true; + } + + void ImageView::Destroy() { + if (data) { + vkDestroyImageView(device, data, 0); + data = 0; + } + device = 0; + } +} diff --git a/src/CRE/Vulkan/image_view.hpp b/src/CRE/Vulkan/image_view.hpp new file mode 100644 index 00000000..89e3f28f --- /dev/null +++ b/src/CRE/Vulkan/image_view.hpp @@ -0,0 +1,30 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#pragma once + +#include "../../KKdLib/default.hpp" +#include + +namespace Vulkan { + class ImageView { + protected: + VkDevice device; + + public: + VkImageView data; + + ImageView(); + ~ImageView(); + + bool Create(VkDevice device, VkImageViewCreateFlags flags, + VkImage image, VkImageViewType view_type, VkFormat format, + VkImageSubresourceRange& sub_resource_range); + bool Create(VkDevice device, VkImageViewCreateFlags flags, + VkImage image, VkImageViewType view_type, VkFormat format, + VkComponentMapping& components, VkImageSubresourceRange& sub_resource_range); + void Destroy(); + }; +} diff --git a/src/CRE/Vulkan/semaphore.cpp b/src/CRE/Vulkan/semaphore.cpp new file mode 100644 index 00000000..f5da41fc --- /dev/null +++ b/src/CRE/Vulkan/semaphore.cpp @@ -0,0 +1,43 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#include "semaphore.hpp" + +namespace Vulkan { + Semaphore::Semaphore() : device(), data() { + + } + + Semaphore::~Semaphore() { + + } + + bool Semaphore::Create(VkDevice device, VkSemaphoreCreateFlags flags) { + if (data) + return true; + + VkSemaphoreCreateInfo semaphore_create_info; + semaphore_create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + semaphore_create_info.pNext = 0; + semaphore_create_info.flags = flags; + + if (vkCreateSemaphore(device, &semaphore_create_info, 0, &data) != VK_SUCCESS) { + this->device = 0; + data = 0; + return false; + } + + this->device = device; + return true; + } + + void Semaphore::Destroy() { + if (data) { + vkDestroySemaphore(device, data, 0); + data = 0; + } + device = 0; + } +} diff --git a/src/CRE/Vulkan/semaphore.hpp b/src/CRE/Vulkan/semaphore.hpp new file mode 100644 index 00000000..b70c11ba --- /dev/null +++ b/src/CRE/Vulkan/semaphore.hpp @@ -0,0 +1,25 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#pragma once + +#include "../../KKdLib/default.hpp" +#include + +namespace Vulkan { + class Semaphore { + protected: + VkDevice device; + + public: + VkSemaphore data; + + Semaphore(); + ~Semaphore(); + + bool Create(VkDevice device, VkSemaphoreCreateFlags flags = 0); + void Destroy(); + }; +} diff --git a/src/CRE/auth_3d.cpp b/src/CRE/auth_3d.cpp index f655b59d..efae5924 100644 --- a/src/CRE/auth_3d.cpp +++ b/src/CRE/auth_3d.cpp @@ -14,7 +14,6 @@ #include "../KKdLib/str_utils.hpp" #include "rob/rob.hpp" #include "clear_color.hpp" -#include "draw_task.hpp" #include "object.hpp" #include "pv_db.hpp" #include "task_effect.hpp" @@ -183,14 +182,10 @@ static void auth_3d_uid_file_modern_unload(auth_3d_uid_file_modern* uid_file); static void auth_3d_uid_file_unload_file(auth_3d_uid_file* uid_file); static void auth_3d_uid_file_modern_unload_file(auth_3d_uid_file_modern* uid_file); -static size_t auth_3d_time_event_radix_index_func_time(auth_3d_time_event* data, size_t index); - -static void Auth3dTestTask__SetAuth3dId(Auth3dTestTask* tt); -static void Auth3dTestTask__SetStage(Auth3dTestTask* tt); +static size_t auth_3d_time_event_radix_index_func_frame(auth_3d_time_event* data, size_t index); auth_3d_data_struct* auth_3d_data; auth_3d_detail::TaskAuth3d* task_auth_3d; -Auth3dTestTask* auth_3d_test_task; static int16_t auth_3d_load_counter; static auth_3d_detail::FrameRateTimeStop frame_rate_time_stop; @@ -438,7 +433,7 @@ void auth_3d::disp(render_context* rctx) { if (i->active) i->Disp(this, &mat, rctx); - rctx->object_data.set_material_list(0, 0); + rctx->disp_manager.set_material_list(0, 0); } void auth_3d::load(a3da* auth_file, @@ -671,7 +666,7 @@ void auth_3d::load(a3da* auth_file, } radix_sort_custom(event_time.data(), event_time.size(), sizeof(auth_3d_time_event), - sizeof(float_t), (radix_index_func)auth_3d_time_event_radix_index_func_time); + sizeof(float_t), (radix_index_func)auth_3d_time_event_radix_index_func_frame); event_time_next = event_time.data(); } @@ -2549,135 +2544,6 @@ auth_3d_data_struct::~auth_3d_data_struct() { } -Auth3dTestTask::Auth3dTestTask::Window::Window() { - stage_link_change = true; - obj_link = true; -} - -Auth3dTestTask::Auth3dTestTask() { - field_1C0 = 10; - field_1C4 = 10; - field_1C8 = 10; - field_1CC = 10; - field_1D0 = false; - field_1D4 = 0; - auth_3d_id = {}; - auth_3d_uid = -1; - repeat = true; - left_right_reverse = false; - field_1E3 = false; - field_1E4 = 0; - field_1E8 = false; - field_1E9 = false; - black_mask_listener = false; - field_1EB = true; - field_1EC = 1; - effcmn_obj_set = -1; - field_210 = 1; - field_328 = false; - field_329 = true; - field_32A = true; - stage_index = -1; - load_stage_index = 0; - trans_value = 0.0f; - rot_y_value = 0.0f; - field_388 = false; - field_38C = 0; - field_390 = false; - field_394 = 0; -} - -Auth3dTestTask:: ~Auth3dTestTask() { - -} - -bool Auth3dTestTask::Init() { - data_struct* aft_data = &data_list[DATA_AFT]; - object_database* aft_obj_db = &aft_data->data_ft.obj_db; - - field_1D0 = false; - field_1D4 = 0; - auth_3d_id = {}; - auth_3d_uid = -1; - repeat = true; - left_right_reverse = false; - field_1E3 = false; - field_1E4 = 0; - field_1E8 = false; - field_1E9 = false; - black_mask_listener = false; - field_1EC = 1; - effcmn_obj_set = aft_obj_db->get_object_set_id("EFFCMN"); - field_210 = 1; - field_328 = false; - field_329 = true; - field_32A = true; - stage_index = -1; - load_stage_index = 0; - trans_value = 0.0f; - rot_y_value = 0.0f; - field_390 = true; - field_394 = 0; - category.clear(); - category.shrink_to_fit(); - load_category.clear(); - load_category.shrink_to_fit(); - obj_sets.clear(); - obj_sets.shrink_to_fit(); - - rctx_ptr->draw_pass.shadow_ptr->self_shadow = true; - clear_color = { 0x99, 0x99, 0x99, 0x00 }; - task_stage_load_task("A3D_STAGE"); - object_storage_load_set(aft_data, aft_obj_db, effcmn_obj_set); - return true; -} - -bool Auth3dTestTask::Ctrl() { - Auth3dTestTask__SetStage(this); - Auth3dTestTask__SetAuth3dId(this); - - if (field_1EC == 1 && !object_storage_load_obj_set_check_not_read(effcmn_obj_set)) - field_1EC = 4; - - if (field_1D4 == 1 && auth_3d_id.check_loaded()) - field_1D4 = 2; - - if (field_1D4 == 2) { - auth_3d_id.set_enable(true); - auth_3d_id.set_paused(false); - auth_3d_id.set_repeat(repeat); - auth_3d_id.set_left_right_reverse(left_right_reverse); - field_1D4 = 4; - } - - if (auth_3d_id.check_not_empty()) { - mat4 mat; - mat4_translate(&trans_value, &mat); - mat4_rotate_y_mult(&mat, rot_y_value * DEG_TO_RAD_FLOAT, &mat); - auth_3d_id.set_mat(mat); - } - return false; -} - -bool Auth3dTestTask::Dest() { - auth_3d_id.unload_id(rctx_ptr); - object_storage_unload_set(effcmn_obj_set); - task_stage_unload_task(); - clear_color = color_black; - rctx_ptr->draw_pass.shadow_ptr->self_shadow = true; - if (category.size()) - auth_3d_data_unload_category(category.c_str()); - category.clear(); - category.shrink_to_fit(); - load_category.clear(); - load_category.shrink_to_fit(); - for (uint32_t& i : obj_sets) - object_storage_unload_set(i); - obj_sets.clear(); - obj_sets.shrink_to_fit(); - return true; -} - void auth_3d_data_init() { auth_3d_data = new auth_3d_data_struct; } @@ -2964,31 +2830,20 @@ void auth_3d_data_free() { delete auth_3d_data; } -void auth_3d_test_task_init() { - auth_3d_test_task = new Auth3dTestTask; -} - -void auth_3d_test_task_free() { - if (auth_3d_test_task) { - delete auth_3d_test_task; - auth_3d_test_task = 0; - } -} - void task_auth_3d_init() { task_auth_3d = new auth_3d_detail::TaskAuth3d; } -bool task_auth_3d_append_task() { - return app::TaskWork::AppendTask(task_auth_3d, "AUTH_3D"); +bool task_auth_3d_add_task() { + return app::TaskWork::AddTask(task_auth_3d, "AUTH_3D"); } bool task_auth_3d_check_task_ready() { return app::TaskWork::CheckTaskReady(task_auth_3d); } -bool task_auth_3d_free_task() { - return task_auth_3d->SetDest(); +bool task_auth_3d_del_task() { + return task_auth_3d->DelTask(); } void task_auth_3d_free() { @@ -3421,7 +3276,7 @@ static void auth_3d_read_file_modern(auth_3d* auth) { } static void auth_3d_set_material_list(auth_3d* auth, render_context* rctx) { - object_data* object_data = &rctx->object_data; + mdl::DispManager& disp_manager = rctx->disp_manager; int32_t mat_list_count = 0; material_list_struct mat_list[TEXTURE_PATTERN_COUNT]; @@ -3463,7 +3318,7 @@ static void auth_3d_set_material_list(auth_3d* auth, render_context* rctx) { mat_list_count++; } - object_data->set_material_list(mat_list_count, mat_list); + disp_manager.set_material_list(mat_list_count, mat_list); } static void auth_3d_ambient_load(auth_3d* auth, auth_3d_ambient* a, auth_3d_ambient_file* af) { @@ -3629,7 +3484,7 @@ static void auth_3d_dof_restore_prev_value(auth_3d_dof* d, render_context* rctx) if (!d->has_dof) return; - rctx->post_process.dof->set_dof_pv(0); + rctx->post_process.dof->set_dof_pv(); } static void auth_3d_dof_set(auth_3d_dof* d, render_context* rctx) { @@ -3975,7 +3830,7 @@ static void auth_3d_m_object_hrc_disp(auth_3d_m_object_hrc* moh, auth_3d* auth, if (!auth->visible || !moh->model_transform.visible) return; - object_data* object_data = &rctx->object_data; + mdl::DispManager& disp_manager = rctx->disp_manager; for (auth_3d_object_instance& i : moh->instance) { if (!i.model_transform.visible) @@ -3990,12 +3845,12 @@ static void auth_3d_m_object_hrc_disp(auth_3d_m_object_hrc* moh, auth_3d* auth, if (auth->alpha < 1.0f) enum_or(flags, auth->draw_task_flags); - object_data->set_draw_task_flags((draw_task_flags)flags); - object_data->set_shadow_type(shadow_type); + disp_manager.set_draw_task_flags((draw_task_flags)flags); + disp_manager.set_shadow_type(shadow_type); - shadow* shad = rctx->draw_pass.shadow_ptr; + shadow* shad = rctx->render_manager.shadow_ptr; if (shad && flags & DRAW_TASK_SHADOW) { - object_data->set_shadow_type(SHADOW_STAGE); + disp_manager.set_shadow_type(SHADOW_STAGE); mat4* m = &moh->model_transform.mat; for (auth_3d_object_node& j : moh->node) @@ -4014,12 +3869,12 @@ static void auth_3d_m_object_hrc_disp(auth_3d_m_object_hrc* moh, auth_3d* auth, } if (i.mats.size()) - draw_task_add_draw_object_by_object_info_object_skin(rctx, + disp_manager.entry_obj_by_object_info_object_skin( i.object_info, 0, 0, auth->alpha, i.mats.data(), 0, 0, &i.model_transform.mat); } - object_data->set_draw_task_flags(); - object_data->set_shadow_type(SHADOW_CHARA); + disp_manager.set_draw_task_flags(); + disp_manager.set_shadow_type(SHADOW_CHARA); } static void auth_3d_m_object_hrc_load(auth_3d* auth, auth_3d_m_object_hrc* moh, @@ -5565,7 +5420,7 @@ static void auth_3d_object_disp(auth_3d_object* o, auth_3d* auth, render_context mat4 mat = o->model_transform.mat; - object_data* object_data = &rctx->object_data; + mdl::DispManager& disp_manager = rctx->disp_manager; object_database* obj_db = auth->obj_db; texture_database* tex_db = auth->tex_db; @@ -5578,7 +5433,7 @@ static void auth_3d_object_disp(auth_3d_object* o, auth_3d* auth, render_context ? &rob_chr->data.adjust_data.item_mat : &rob_chr->data.adjust_data.mat, &m); mat4_mult(&m, &mat, &mat); - object_data->set_shadow_type(auth->chara_id ? SHADOW_STAGE : SHADOW_CHARA); + disp_manager.set_shadow_type(auth->chara_id ? SHADOW_STAGE : SHADOW_CHARA); } draw_task_flags flags = (draw_task_flags)0; @@ -5589,7 +5444,7 @@ static void auth_3d_object_disp(auth_3d_object* o, auth_3d* auth, render_context if (o->refract) enum_or(flags, DRAW_TASK_NO_TRANSLUCENCY | DRAW_TASK_REFRACT); - object_data->set_draw_task_flags((draw_task_flags)flags); + disp_manager.set_draw_task_flags((draw_task_flags)flags); char buf[0x80]; int32_t tex_pat_count = 0; @@ -5616,7 +5471,7 @@ static void auth_3d_object_disp(auth_3d_object* o, auth_3d* auth, render_context } if (tex_pat_count) - object_data->set_texture_pattern(tex_pat_count, tex_pat); + disp_manager.set_texture_pattern(tex_pat_count, tex_pat); int32_t tex_trans_count = 0; texture_transform_struct tex_trans[TEXTURE_TRANSFORM_COUNT]; @@ -5631,13 +5486,13 @@ static void auth_3d_object_disp(auth_3d_object* o, auth_3d* auth, render_context } if (tex_trans_count) - object_data->set_texture_transform(tex_trans_count, tex_trans); + disp_manager.set_texture_transform(tex_trans_count, tex_trans); const char* uid_name = o->uid_name.c_str(); int32_t uid_name_length = (int32_t)o->uid_name.size(); if (uid_name_length <= 3) - draw_task_add_draw_object_by_object_info(rctx, &mat, o->object_info); + disp_manager.entry_obj_by_object_info(&mat, o->object_info); else if (o->morph.curve) { float_t morph = o->morph.value; int32_t morph_int = (int32_t)morph; @@ -5647,12 +5502,12 @@ static void auth_3d_object_disp(auth_3d_object* o, auth_3d* auth, render_context object_info morph_obj_info = obj_db->get_object_info(buf); if (morph_obj_info.is_null()) morph_obj_info = o->object_info; - object_data->set_morph(morph_obj_info, morph); + disp_manager.set_morph(morph_obj_info, morph); sprintf_s(buf, sizeof(buf), "%.*s%03d", uid_name_length - 3, uid_name, morph_int); object_info obj_info = obj_db->get_object_info(buf); - draw_task_add_draw_object_by_object_info(rctx, &mat, obj_info); - object_data->set_morph({}, 0.0f); + disp_manager.entry_obj_by_object_info(&mat, obj_info); + disp_manager.set_morph({}, 0.0f); } else { if (morph >= 1.0f) @@ -5662,7 +5517,7 @@ static void auth_3d_object_disp(auth_3d_object* o, auth_3d* auth, render_context object_info obj_info = obj_db->get_object_info(buf); if (obj_info.is_null()) obj_info = o->object_info; - draw_task_add_draw_object_by_object_info(rctx, &mat, obj_info); + disp_manager.entry_obj_by_object_info(&mat, obj_info); } } else if (o->pattern.curve) { @@ -5674,14 +5529,14 @@ static void auth_3d_object_disp(auth_3d_object* o, auth_3d* auth, render_context sprintf_s(buf, sizeof(buf), "%.*s%03d", uid_name_length - 3, uid_name, (int32_t)pat); object_info obj_info = obj_db->get_object_info(buf); - draw_task_add_draw_object_by_object_info(rctx, &mat, obj_info); + disp_manager.entry_obj_by_object_info(&mat, obj_info); } else - draw_task_add_draw_object_by_object_info(rctx, &mat, o->object_info); + disp_manager.entry_obj_by_object_info(&mat, o->object_info); - object_data->set_texture_transform(); - object_data->set_texture_pattern(); - object_data->set_draw_task_flags(); + disp_manager.set_texture_transform(); + disp_manager.set_texture_pattern(); + disp_manager.set_draw_task_flags(); for (auth_3d_object*& i : o->children_object) auth_3d_object_disp(i, auth, rctx); @@ -5738,7 +5593,7 @@ static void auth_3d_object_hrc_disp(auth_3d_object_hrc* oh, auth_3d* auth, rende if (!auth->visible || !oh->node.front().model_transform.visible) return; - object_data* object_data = &rctx->object_data; + mdl::DispManager& disp_manager = rctx->disp_manager; object_database* obj_db = auth->obj_db; texture_database* tex_db = auth->tex_db; @@ -5752,8 +5607,8 @@ static void auth_3d_object_hrc_disp(auth_3d_object_hrc* oh, auth_3d* auth, rende if (auth->alpha < 1.0f) enum_or(flags, auth->draw_task_flags); - object_data->set_draw_task_flags(flags); - object_data->set_shadow_type(SHADOW_CHARA); + disp_manager.set_draw_task_flags(flags); + disp_manager.set_shadow_type(SHADOW_CHARA); mat4 mat = mat4_identity; if (auth->chara_id >= 0 && auth->chara_id < ROB_CHARA_COUNT) { @@ -5764,11 +5619,11 @@ static void auth_3d_object_hrc_disp(auth_3d_object_hrc* oh, auth_3d* auth, rende ? &rob_chr->data.adjust_data.item_mat : &rob_chr->data.adjust_data.mat, &mat); if (auth->chara_id) - object_data->set_shadow_type(SHADOW_STAGE); + disp_manager.set_shadow_type(SHADOW_STAGE); } } else if (flags & DRAW_TASK_SHADOW) { - object_data->set_shadow_type(SHADOW_STAGE); + disp_manager.set_shadow_type(SHADOW_STAGE); mat4* m = &oh->node.front().model_transform.mat; for (auth_3d_object_node& i : oh->node) @@ -5777,7 +5632,7 @@ static void auth_3d_object_hrc_disp(auth_3d_object_hrc* oh, auth_3d* auth, rende break; } - shadow* shad = rctx->draw_pass.shadow_ptr; + shadow* shad = rctx->render_manager.shadow_ptr; if (shad) { vec3 pos = *(vec3*)&m->row3; pos.y -= 0.2f; @@ -5786,11 +5641,11 @@ static void auth_3d_object_hrc_disp(auth_3d_object_hrc* oh, auth_3d* auth, rende } if (oh->mats.size()) - draw_task_add_draw_object_by_object_info_object_skin(rctx, + disp_manager.entry_obj_by_object_info_object_skin( oh->object_info, 0, 0, auth->alpha, oh->mats.data(), 0, 0, &mat); - object_data->set_draw_task_flags(); - object_data->set_shadow_type(SHADOW_CHARA); + disp_manager.set_draw_task_flags(); + disp_manager.set_shadow_type(SHADOW_CHARA); for (auth_3d_object*& i : oh->children_object) auth_3d_object_disp(i, auth, rctx); @@ -6486,123 +6341,6 @@ static void auth_3d_uid_file_modern_unload_file(auth_3d_uid_file_modern* uid_fil auth_3d_data->uid_files_modern.erase(uid_file->hash); } -static size_t auth_3d_time_event_radix_index_func_time(auth_3d_time_event* data, size_t index) { +static size_t auth_3d_time_event_radix_index_func_frame(auth_3d_time_event* data, size_t index) { return *(uint32_t*)&data[index].frame; } - -static void Auth3dTestTask__SetAuth3dId(Auth3dTestTask* tt) { - if (task_stage_check_not_loaded() || tt->auth_3d_uid == -1 - || !tt->load_category.size() || tt->auth_3d_uid == tt->auth_3d_id.get_uid()) - return; - - if (tt->load_category.compare(tt->category)) { - if (tt->category.size()) { - auth_3d_data_unload_category(tt->category.c_str()); - for (uint32_t& i : tt->obj_sets) - object_storage_unload_set(i); - tt->obj_sets.clear(); - } - auth_3d_data_load_category(tt->load_category.c_str()); - tt->category.assign(tt->load_category); - if (tt->window.obj_link) - tt->field_394 = 1; - } - - if (!auth_3d_data_check_category_loaded(tt->category.c_str())) - return; - - if (tt->field_394 == 1) { - data_struct* aft_data = &data_list[DATA_AFT]; - auth_3d_database* aft_auth_3d_db = &aft_data->data_ft.auth_3d_db; - object_database* aft_obj_db = &aft_data->data_ft.obj_db; - - auth_3d_data_get_obj_sets_from_category(tt->category, tt->obj_sets, aft_auth_3d_db, aft_obj_db); - - for (uint32_t& i : tt->obj_sets) - object_storage_load_set(aft_data, aft_obj_db, i); - tt->field_394 = 2; - } - else if (tt->field_394 == 2) { - bool wait_load = false; - for (uint32_t& i : tt->obj_sets) - if (object_storage_load_obj_set_check_not_read(i)) - wait_load = true; - - if (!wait_load) - tt->field_394 = 0; - } - - if (!tt->field_394) { - data_struct* aft_data = &data_list[DATA_AFT]; - auth_3d_database* aft_auth_3d_db = &aft_data->data_ft.auth_3d_db; - - tt->auth_3d_id.unload_id(rctx_ptr); - tt->auth_3d_id = auth_3d_data_load_uid(tt->auth_3d_uid, aft_auth_3d_db); - if (tt->auth_3d_id.check_not_empty()) { - tt->auth_3d_id.set_enable(false); - tt->auth_3d_id.read_file(aft_auth_3d_db); - tt->field_1D4 = 1; - } - tt->auth_3d_uid = -1; - return; - } -} - -static void Auth3dTestTask__SetStage(Auth3dTestTask* tt) { - if (tt->window.stage_link_change) { - 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 char* name = auth_3d_data_get_uid_name(tt->auth_3d_uid, aft_auth_3d_db); - size_t name_length = utf8_length(name); - if (name && name_length >= 3) { - size_t v5 = name_length - 2; - const char* v6 = name; - while (v5) { - const char* v7 = (const char*)memchr(v6, 'S', v5); - if (!v7) - break; - - if (memcmp(v7, "STG", min_def(v5, 3))) { - v5 += v6 - v7 - 1; - v6 = v7 + 1; - continue; - } - - size_t v16 = v7 - name; - if (v16 == -1 || v16 >= name_length || name_length == v16) - break; - - size_t v17 = name_length - v16; - for (const char* i = name + v16; v17; ) { - const char* v20 = (const char*)memchr(i, '_', v17); - if (!v20) - break; - - if (*v20 != '_') { - v17 += i - v20 - 1; - i = v20 + 1; - continue; - } - - size_t v24 = v20 - name; - if (v24 != -1) { - std::string v30 = std::string(name + v16, v24 - v16); - int32_t stage_index = aft_stage_data->get_stage_index(v30.c_str()); - if (tt->stage_index != stage_index) - tt->load_stage_index = stage_index; - } - break; - } - break; - } - } - } - - if (tt->load_stage_index != -1 && tt->load_stage_index != tt->stage_index) { - task_stage_set_stage_index(tt->load_stage_index); - tt->stage_index = tt->load_stage_index; - tt->load_stage_index = -1; - } -} diff --git a/src/CRE/auth_3d.hpp b/src/CRE/auth_3d.hpp index 96d90085..47de5ac5 100644 --- a/src/CRE/auth_3d.hpp +++ b/src/CRE/auth_3d.hpp @@ -974,60 +974,7 @@ struct auth_3d_data_struct { ~auth_3d_data_struct(); }; -class Auth3dTestTask : public app::Task { -public: - int32_t field_1C0; - int32_t field_1C4; - int32_t field_1C8; - int32_t field_1CC; - bool field_1D0; - int32_t field_1D4; - ::auth_3d_id auth_3d_id; - int32_t auth_3d_uid; - bool repeat; - bool left_right_reverse; - bool field_1E3; - int32_t field_1E4; - bool field_1E8; - bool field_1E9; - bool black_mask_listener; - bool field_1EB; - int32_t field_1EC; - uint32_t effcmn_obj_set; - int32_t field_210; - bool field_328; - bool field_329; - bool field_32A; - int32_t stage_index; - int32_t load_stage_index; - vec3 trans_value; - float_t rot_y_value; - std::vector> field_348; - bool field_388; - int32_t field_38C; - bool field_390; - int32_t field_394; - std::string category; - std::string load_category; - std::vector obj_sets; - - struct Window { - bool stage_link_change; - bool obj_link; - - Window(); - } window; - - Auth3dTestTask(); - virtual ~Auth3dTestTask() override; - - virtual bool Init() override; - virtual bool Ctrl() override; - virtual bool Dest() override; -}; - extern auth_3d_data_struct* auth_3d_data; -extern Auth3dTestTask* auth_3d_test_task; extern void auth_3d_data_init(); extern bool auth_3d_data_check_category_loaded(const char* category_name); @@ -1049,11 +996,8 @@ extern void auth_3d_data_unload_category(const char* category_name); extern void auth_3d_data_unload_category(uint32_t category_hash); extern void auth_3d_data_free(); -extern void auth_3d_test_task_init(); -extern void auth_3d_test_task_free(); - extern void task_auth_3d_init(); -extern bool task_auth_3d_append_task(); +extern bool task_auth_3d_add_task(); extern bool task_auth_3d_check_task_ready(); -extern bool task_auth_3d_free_task(); +extern bool task_auth_3d_del_task(); extern void task_auth_3d_free(); diff --git a/src/CRE/camera.cpp b/src/CRE/camera.cpp index cff57764..639839c2 100644 --- a/src/CRE/camera.cpp +++ b/src/CRE/camera.cpp @@ -146,6 +146,11 @@ void camera::get_view_point(vec3& value) { value = view_point; } +void camera::get_view_point(vec4& value) { + *(vec3*)&value = view_point; + value.w = 0.0f; +} + void camera::set_view_point(const vec3& value) { if (value != view_point) { view_point = value; diff --git a/src/CRE/camera.hpp b/src/CRE/camera.hpp index 1a664d57..83fe986d 100644 --- a/src/CRE/camera.hpp +++ b/src/CRE/camera.hpp @@ -71,6 +71,7 @@ struct camera { double_t get_roll(); void set_roll(double_t value); void get_view_point(vec3& value); + void get_view_point(vec4& value); void set_view_point(const vec3& value); void set_view_point(const vec3&& value); void get_interest(vec3& value); diff --git a/src/CRE/data.cpp b/src/CRE/data.cpp index e4f197a3..649a96ac 100644 --- a/src/CRE/data.cpp +++ b/src/CRE/data.cpp @@ -60,10 +60,10 @@ void data_struct_load_db() { data_struct* ds = &data_list[DATA_AFT]; data_ft* d = &ds->data_ft; - std::list& prefixes = mdata_manager_get()->prefixes; - - for (std::string& i : prefixes) { - std::string file = i + "auth_3d_db.bin"; + for (const std::string& i : mdata_manager_get()->GetPrefixes()) { + std::string file; + file.assign(i); + file.append("auth_3d_db.bin"); auth_3d_database_file auth_3d_db_file; ds->load_file(&auth_3d_db_file, "rom/auth_3d/", file.c_str(), @@ -79,8 +79,10 @@ void data_struct_load_db() { "bone_data.bin", bone_database::load_file); } - for (std::string& i : prefixes) { - std::string file = i + "mot_db.farc"; + for (const std::string& i : mdata_manager_get()->GetPrefixes()) { + std::string file; + file.assign(i); + file.append("mot_db.farc"); motion_database_file mot_db_file; ds->load_file(&mot_db_file, "rom/rob/", file.c_str(), @@ -88,8 +90,10 @@ void data_struct_load_db() { d->mot_db.add(&mot_db_file); } - for (std::string& i : prefixes) { - std::string file = i + "obj_db.bin"; + for (const std::string& i : mdata_manager_get()->GetPrefixes()) { + std::string file; + file.assign(i); + file.append("obj_db.bin"); object_database_file obj_db_file; obj_db_file.modern = false; @@ -98,8 +102,10 @@ void data_struct_load_db() { d->obj_db.add(&obj_db_file); } - for (std::string& i : prefixes) { - std::string file = i + "spr_db.bin"; + for (const std::string& i : mdata_manager_get()->GetPrefixes()) { + std::string file; + file.assign(i); + file.append("spr_db.bin"); sprite_database_file spr_db_file; spr_db_file.modern = false; @@ -108,8 +114,10 @@ void data_struct_load_db() { d->spr_db.add(&spr_db_file); } - for (std::string& i : prefixes) { - std::string file = i + "stage_data.bin"; + for (const std::string& i : mdata_manager_get()->GetPrefixes()) { + std::string file; + file.assign(i); + file.append("stage_data.bin"); stage_database_file stage_data_file; stage_data_file.modern = false; @@ -118,8 +126,10 @@ void data_struct_load_db() { d->stage_data.add(&stage_data_file); } - for (std::string& i : prefixes) { - std::string file = i + "tex_db.bin"; + for (const std::string& i : mdata_manager_get()->GetPrefixes()) { + std::string file; + file.assign(i); + file.append("tex_db.bin"); texture_database_file tex_db_file; tex_db_file.modern = false; diff --git a/src/CRE/draw_object.cpp b/src/CRE/draw_object.cpp index 65780c15..7e7a08ea 100644 --- a/src/CRE/draw_object.cpp +++ b/src/CRE/draw_object.cpp @@ -9,102 +9,166 @@ #include "static_var.hpp" #include "texture.hpp" -#define enable_vertex_attrib_array(index) \ -glEnableVertexAttribArray(index); \ -draw->vertex_attrib_array[index] = true - static bool draw_object_blend_set(render_context* rctx, - draw_object* draw, obj_material_shader_lighting_type lighting_type); + const mdl::ObjSubMeshArgs* args, obj_material_shader_lighting_type lighting_type); static void draw_object_chara_color_fog_set(render_context* rctx, - draw_object* draw, bool disable_fog); -static void draw_object_material_reset_default(obj_material_data* mat_data); + const mdl::ObjSubMeshArgs* args, bool disable_fog); +static void draw_object_material_reset_default(const obj_material_data* mat_data); static void draw_object_material_reset_reflect(); static void draw_object_material_set_default(render_context* rctx, - draw_object* draw, bool use_shader); + const mdl::ObjSubMeshArgs* args, bool use_shader); static void draw_object_material_set_parameter(render_context* rctx, - obj_material_data* mat_data); -static void draw_object_material_set_reflect(render_context* rctx, draw_object* draw); -static void draw_object_material_set_uniform(render_context* rctx, - obj_material_data* mat_data, bool disable_color_l); -static void draw_object_vertex_attrib_reset_default(draw_object* draw); -static void draw_object_vertex_attrib_reset_reflect(draw_object* draw); -static void draw_object_vertex_attrib_set_default(draw_object* draw); -static void draw_object_vertex_attrib_set_reflect(draw_object* draw); + const obj_material_data* mat_data); +static void draw_object_material_set_reflect(render_context* rctx, const mdl::ObjSubMeshArgs* args); +static void draw_object_material_set_uniform(const obj_material_data* mat_data, bool disable_color_l); +static void draw_object_vertex_attrib_reset_default(const mdl::ObjSubMeshArgs* args); +static void draw_object_vertex_attrib_reset_reflect(const mdl::ObjSubMeshArgs* args); +static void draw_object_vertex_attrib_set_default(render_context* rctx, const mdl::ObjSubMeshArgs* args); +static void draw_object_vertex_attrib_set_reflect(render_context* rctx, const mdl::ObjSubMeshArgs* args); -texture_data_struct::texture_data_struct() : field_0() { +namespace mdl { + void draw(render_context* rctx, obj_primitive_type primitive_type, uint32_t count, + uint16_t start, uint16_t end, obj_index_format index_format, size_t indices) { + GLenum mesh_draw_mode[] = { + GL_ZERO, //GL_POINTS, + GL_LINES, + GL_LINE_STRIP, + GL_LINE_LOOP, + GL_TRIANGLES, + GL_TRIANGLE_STRIP, + GL_TRIANGLE_FAN, + GL_ZERO, //GL_QUADS, + GL_ZERO, //GL_QUAD_STRIP, + GL_ZERO, //GL_POLYGON, + }; -} + GLenum mesh_indices_type[] = { + GL_ZERO, + GL_UNSIGNED_SHORT, + GL_UNSIGNED_INT, + }; -void draw_object_draw(render_context* rctx, draw_object* draw, mat4* model, - void(*draw_object_func)(render_context* rctx, draw_object* draw), int32_t show_vector) { - GLuint vao = rctx->object_data.get_vertex_array(draw); - if (!vao) + rctx->obj_batch_ubo.WriteMapMemory(rctx->obj_batch); + if (primitive_type == OBJ_PRIMITIVE_TRIANGLE_STRIP && index_format == OBJ_INDEX_U16) + shaders_ft.draw_range_elements(GL_TRIANGLE_STRIP, + start, end, count, GL_UNSIGNED_SHORT, (void*)indices); + else + shaders_ft.draw_elements(mesh_draw_mode[primitive_type], + count, mesh_indices_type[index_format], (void*)indices); + + rctx->draw_state.stats.draw_count += count; + if (primitive_type == OBJ_PRIMITIVE_TRIANGLES) + rctx->draw_state.stats.draw_triangle_count += count / 3; + else if (primitive_type == OBJ_PRIMITIVE_TRIANGLE_STRIP) + rctx->draw_state.stats.draw_triangle_count += count - 2; + } + + void draw_sub_mesh(render_context* rctx, const ObjSubMeshArgs* args, const mat4* model, + void(*func)(render_context* rctx, const ObjSubMeshArgs* args)) { + GLuint vao = rctx->disp_manager.get_vertex_array(args); + if (!vao) + return; + + if (args->mats) { + const mat4* mats = args->mats; + rctx->obj_batch.set_g_joint(mats[0]); + + vec4* g_joint_transforms = rctx->obj_skinning.g_joint_transforms; + + mat4 mat; + for (int32_t i = 0; i < args->mat_count; i++, mats++, g_joint_transforms += 3) { + mat4_transpose(mats, &mat); + g_joint_transforms[0] = mat.row0; + g_joint_transforms[1] = mat.row1; + g_joint_transforms[2] = mat.row2; + } + + rctx->obj_skinning_ubo.WriteMapMemory(rctx->obj_skinning); + + rctx->obj_batch.set_transforms(mat4_identity, rctx->view_mat, rctx->proj_mat); + + uniform_value[U_BONE_MAT] = 1; + gl_state_bind_vertex_array(vao); + func(rctx, args); + uniform_value[U_BONE_MAT] = 0; + } + else { + mat4 mat; + if (args->mesh->attrib.m.billboard) + model_mat_face_camera_view(&rctx->view_mat, model, &mat); + else if (args->mesh->attrib.m.billboard_y_axis) + model_mat_face_camera_position(&rctx->view_mat, model, &mat); + else + mat = *model; + + uniform_value[U_BONE_MAT] = 0; + gl_state_bind_vertex_array(vao); + if (func != draw_sub_mesh_default || !args->instances_count) { + draw_object_model_mat_load(rctx, mat); + func(rctx, args); + } + else + draw_sub_mesh_default_instanced(rctx, args, &mat); + uniform_value[U_BONE_MAT] = 0; + } + } + + /*void draw_sub_mesh_show_vector(render_context* rctx, + const ObjSubMeshArgs* args, mat4* model, int32_t show_vector) { return; + }*/ - if (draw->mats) { - mat4* mats = draw->mats; - if (draw->mat_count == 2) - shaders_ft.state_matrix_set_program(7, *mats); - - mat4 mat; - for (int32_t i = 0; i < draw->mat_count; i++, mats++) { - mat4_transpose(mats, &mat); - shaders_ft.buffer_set((size_t)i * 3, 3, &mat.row0); + void draw_sub_mesh_default(render_context* rctx, const ObjSubMeshArgs* args) { + if (args->set_blend_color) { + rctx->obj_batch.g_blend_color = args->blend_color; + rctx->obj_batch.g_offset_color = 0.0f; } - shaders_ft.state_matrix_set_modelview(0, rctx->view_mat, true); - shaders_ft.state_matrix_set_program(6, mat4_identity); - uniform_value[U_BONE_MAT] = 1; - } - else { - mat4 mat; - if (draw->mesh->attrib.m.billboard) - model_mat_face_camera_view(&rctx->camera->inv_view_rot, model, &mat); - else if (draw->mesh->attrib.m.billboard_y_axis) - model_mat_face_camera_position(&rctx->camera->view, model, &mat); - else - mat = *model; - draw_object_model_mat_load(rctx, &mat); - uniform_value[U_BONE_MAT] = 0; - } + if (!args->func) + draw_object_vertex_attrib_set_default(rctx, args); + draw_object_material_set_default(rctx, args, rctx->draw_state.shader); - if (!show_vector) { - gl_state_bind_vertex_array(vao); - draw_object_func(rctx, draw); - } - uniform_value[U_BONE_MAT] = 0; -} - -void draw_object_draw_default(render_context* rctx, draw_object* draw) { - if (draw->set_blend_color) { - shaders_ft.env_vert_set(3, draw->blend_color); - shaders_ft.env_vert_set(4, 0.0f); - } - - if (!draw->draw_object_func) - draw_object_vertex_attrib_set_default(draw); - - draw_object_material_set_default(rctx, draw, rctx->draw_state.shader); - - obj_sub_mesh* sub_mesh = draw->sub_mesh; - if (!draw->instances_count) - obj_sub_mesh_draw(rctx, + const obj_sub_mesh* sub_mesh = args->sub_mesh; + draw(rctx, sub_mesh->primitive_type, sub_mesh->num_index, sub_mesh->first_index, sub_mesh->last_index, sub_mesh->index_format, sub_mesh->index_offset); - else { - mat4 mat; - for (int32_t i = 0; i < draw->instances_count; i++) { - mat4_transpose(&draw->instances_mat[i], &mat); - glVertexAttrib4fv(12, (const GLfloat*)&mat.row0); - glVertexAttrib4fv(13, (const GLfloat*)&mat.row1); - glVertexAttrib4fv(14, (const GLfloat*)&mat.row2); - glVertexAttrib4fv(15, (const GLfloat*)&mat.row3); - obj_sub_mesh_draw(rctx, + + draw_object_material_reset_default(args->material); + if (!args->func) + draw_object_vertex_attrib_reset_default(args); + + if (args->set_blend_color) { + rctx->obj_batch.g_blend_color = 1.0f; + rctx->obj_batch.g_offset_color = 0.0f; + } + + rctx->draw_state.stats.object_draw_count++; + } + + void draw_sub_mesh_default_instanced(render_context* rctx, + const ObjSubMeshArgs* args, const mat4* mat) { + if (args->set_blend_color) { + rctx->obj_batch.g_blend_color = args->blend_color; + rctx->obj_batch.g_offset_color = 0.0f; + } + + if (!args->func) + draw_object_vertex_attrib_set_default(rctx, args); + + draw_object_material_set_default(rctx, args, rctx->draw_state.shader); + + const obj_sub_mesh* sub_mesh = args->sub_mesh; + const mat4* instances_mat = args->instances_mat; + mat4 _mat; + for (int32_t i = args->instances_count; i >= 0; i--, instances_mat++) { + mat4_mult(instances_mat, mat, &_mat); + draw_object_model_mat_load(rctx, _mat); + draw(rctx, sub_mesh->primitive_type, sub_mesh->num_index, sub_mesh->first_index, @@ -112,211 +176,213 @@ void draw_object_draw_default(render_context* rctx, draw_object* draw) { sub_mesh->index_format, sub_mesh->index_offset); } + + draw_object_material_reset_default(args->material); + if (!args->func) + draw_object_vertex_attrib_reset_default(args); + + if (args->set_blend_color) { + rctx->obj_batch.g_blend_color = 1.0f; + rctx->obj_batch.g_offset_color = 0.0f; + } + + rctx->draw_state.stats.object_draw_count++; } - draw_object_material_reset_default(draw->material); - if (!draw->draw_object_func) - draw_object_vertex_attrib_reset_default(draw); + void draw_sub_mesh_sss(render_context* rctx, const ObjSubMeshArgs* args) { + obj_material_attrib_member attrib = args->material->material.attrib.m; + uniform_value[U_ALPHA_TEST] = (!attrib.flag_28 && (args->blend_color.w < 1.0f + || (attrib.alpha_texture || attrib.alpha_material) && !attrib.punch_through + || args->sub_mesh->attrib.m.transparent) + || attrib.punch_through) ? 1 : 0; - if (draw->set_blend_color) { - shaders_ft.env_vert_set(3, 1.0f); - shaders_ft.env_vert_set(4, 0.0f); - } - - rctx->draw_state.stats.object_draw_count++; -} - -void draw_object_draw_sss(render_context* rctx, draw_object* draw) { - obj_material_attrib_member attrib = draw->material->material.attrib.m; - uniform_value[U_ALPHA_TEST] = (!attrib.flag_28 && (draw->blend_color.w < 1.0f - || (attrib.alpha_texture || attrib.alpha_material) && !attrib.punch_through - || draw->sub_mesh->attrib.m.transparent) - || attrib.punch_through) ? 1 : 0; - - uniform_value[U26] = 1; - bool aniso = false; - obj_material_data* material = draw->material; - switch (material->material.shader.index) { - case SHADER_FT_CLOTH: - if (!rctx->draw_pass.npr_param && material->material.color.ambient.w < 1.0f - && material->material.shader_info.m.aniso_direction == OBJ_MATERIAL_ANISO_DIRECTION_NORMAL) + uniform_value[U26] = 1; + bool aniso = false; + const obj_material_data* material = args->material; + switch (material->material.shader.index) { + case SHADER_FT_CLOTH: + if (!rctx->render_manager.npr_param && material->material.color.ambient.w < 1.0f + && material->material.shader_info.m.aniso_direction == OBJ_MATERIAL_ANISO_DIRECTION_NORMAL) + aniso = true; + break; + case SHADER_FT_TIGHTS: + if (!rctx->render_manager.npr_param) + aniso = true; + break; + //case SHADER_FT_EYEBALL: + case SHADER_FT_GLASEYE: + uniform_value[U26] = 0; aniso = true; - break; - case SHADER_FT_TIGHTS: - if (!rctx->draw_pass.npr_param) + break; + case SHADER_FT_SKIN: aniso = true; - break; - case SHADER_FT_EYEBALL: - uniform_value[U26] = 0; - aniso = true; - break; - case SHADER_FT_SKIN: - aniso = true; - break; - } + break; + } - if (aniso) { - shaders_ft.env_frag_set(25, 0.0f, 0.0f, 0.0f, 0.5f); - uniform_value[U37] = 1; - } - else { - vec4& sss_param = rctx->draw_pass.sss_data.param; - shaders_ft.env_frag_set(25, sss_param.x, sss_param.y, sss_param.z, 0.5f); - uniform_value[U37] = 0; - } - draw_object_draw_default(rctx, draw); -} - -void draw_object_draw_reflect(render_context* rctx, draw_object* draw) { - obj_material_data* material = draw->material; - draw_object_vertex_attrib_set_reflect(draw); - obj_material_shader_lighting_type lighting_type - = material->material.shader_info.get_lighting_type(); - bool disable_fog = draw_object_blend_set(rctx, draw, lighting_type); - draw_object_chara_color_fog_set(rctx, draw, disable_fog); - draw_object_material_set_reflect(rctx, draw); - - obj_sub_mesh* sub_mesh = draw->sub_mesh; - if (sub_mesh->index_format != OBJ_INDEX_U8) - obj_sub_mesh_draw(rctx, - sub_mesh->primitive_type, - sub_mesh->num_index, - sub_mesh->first_index, - sub_mesh->last_index, - sub_mesh->index_format, - sub_mesh->index_offset); - - draw_object_material_reset_reflect(); - draw_object_vertex_attrib_reset_reflect(draw); - uniform_value_reset(); - - rctx->draw_state.stats.object_reflect_draw_count++; -} - -void draw_object_draw_reflect_reflect_map(render_context* rctx, draw_object* draw) { - draw_object_vertex_attrib_set_reflect(draw); - draw_object_material_set_reflect(rctx, draw); - - obj_sub_mesh* sub_mesh = draw->sub_mesh; - if (sub_mesh->index_format != OBJ_INDEX_U8) - obj_sub_mesh_draw(rctx, - sub_mesh->primitive_type, - sub_mesh->num_index, - sub_mesh->first_index, - sub_mesh->last_index, - sub_mesh->index_format, - sub_mesh->index_offset); - - draw_object_material_reset_reflect(); - draw_object_vertex_attrib_reset_reflect(draw); - - rctx->draw_state.stats.object_reflect_draw_count++; -} - -void draw_object_draw_shadow(render_context* rctx, draw_object* draw) { - obj_material_attrib_member attrib = draw->material->material.attrib.m; - if (!attrib.flag_28 && (draw->blend_color.w < 1.0f - || (attrib.alpha_texture || attrib.alpha_material) && !attrib.punch_through - || draw->sub_mesh->attrib.m.transparent) - || attrib.punch_through) { - uniform_value[U_ALPHA_TEST] = 1; - draw_object_draw_translucent(rctx, draw); - } - else { - uniform_value[U_ALPHA_TEST] = 0; - draw_object_draw_translucent(rctx, draw); - } -} - -void draw_object_draw_translucent(render_context* rctx, draw_object* draw) { - obj_material_data* material = draw->material; - std::vector* textures = draw->textures; - if (rctx->draw_state.shader_index != -1) { - shaders_ft.state_material_set_emission(false, draw->emission); - draw_object_material_set_uniform(rctx, material, 0); - if (material->material.attrib.m.alpha_texture) - uniform_value[U_TEXTURE_COUNT] = 0; - shaders_ft.set(rctx->draw_state.shader_index); - } - - draw_object_vertex_attrib_set_default(draw); - if (draw->material->material.attrib.m.double_sided) - gl_state_disable_cull_face(); - - GLuint tex_id = -1; - int32_t tex_index = 0; - if (material->material.attrib.m.alpha_texture) { - obj_material_texture_data* texdata = material->material.texdata; - uint32_t texture_id = -1; - uint32_t texture_index = -1; - if (!material->material.shader_compo.m.transparency) { - for (int32_t i = 0; i < 8; i++, texdata++) - if (texdata->shader_info.m.tex_type == OBJ_MATERIAL_TEXTURE_COLOR) { - texture_id = texdata->tex_index; - texture_index = texdata->texture_index; - break; - } - tex_index = 0; + if (aniso) { + rctx->obj_batch.g_sss_param = { 0.0f, 0.0f, 0.0f, 0.5f }; + uniform_value[U37] = 1; } else { - for (int32_t i = 0; i < 8; i++, texdata++) - if (texdata->shader_info.m.tex_type == OBJ_MATERIAL_TEXTURE_TRANSPARENCY) { - if (texdata->attrib.m.flag_29) { - texture_id = texdata->tex_index; - texture_index = texdata->texture_index; - } - break; - } - tex_index = 4; + vec4& sss_param = rctx->render_manager.sss_data.param; + rctx->obj_batch.g_sss_param = { sss_param.x, sss_param.y, sss_param.z, 0.5f }; + uniform_value[U37] = 0; } + draw_sub_mesh_default(rctx, args); + } - if (texture_id != -1) { - for (int32_t j = 0; j < draw->texture_pattern_count; j++) - if (draw->texture_pattern_array[j].src == ::texture_id(0, texture_id)) { - texture* tex = texture_storage_get_texture(draw->texture_pattern_array[j].dst); - if (tex) - tex_id = tex->tex; - break; - } + void draw_sub_mesh_reflect(render_context* rctx, const ObjSubMeshArgs* args) { + const obj_material_data* material = args->material; + draw_object_vertex_attrib_set_reflect(rctx, args); + obj_material_shader_lighting_type lighting_type + = material->material.shader_info.get_lighting_type(); + bool disable_fog = draw_object_blend_set(rctx, args, lighting_type); + draw_object_chara_color_fog_set(rctx, args, disable_fog); + draw_object_material_set_reflect(rctx, args); - if (tex_id == -1) - tex_id = (*textures)[texdata->texture_index]; + const obj_sub_mesh* sub_mesh = args->sub_mesh; + if (sub_mesh->index_format != OBJ_INDEX_U8) + draw(rctx, + sub_mesh->primitive_type, + sub_mesh->num_index, + sub_mesh->first_index, + sub_mesh->last_index, + sub_mesh->index_format, + sub_mesh->index_offset); - if (tex_id == -1) - tex_id = 0; + draw_object_material_reset_reflect(); + draw_object_vertex_attrib_reset_reflect(args); + uniform_value_reset(); - gl_state_active_bind_texture_2d(tex_index, tex_id); + rctx->draw_state.stats.object_reflect_draw_count++; + } + + void draw_sub_mesh_reflect_reflect_map(render_context* rctx, const ObjSubMeshArgs* args) { + draw_object_vertex_attrib_set_reflect(rctx, args); + draw_object_material_set_reflect(rctx, args); + + const obj_sub_mesh* sub_mesh = args->sub_mesh; + if (sub_mesh->index_format != OBJ_INDEX_U8) + draw(rctx, + sub_mesh->primitive_type, + sub_mesh->num_index, + sub_mesh->first_index, + sub_mesh->last_index, + sub_mesh->index_format, + sub_mesh->index_offset); + + draw_object_material_reset_reflect(); + draw_object_vertex_attrib_reset_reflect(args); + + rctx->draw_state.stats.object_reflect_draw_count++; + } + + void draw_sub_mesh_shadow(render_context* rctx, const ObjSubMeshArgs* args) { + obj_material_attrib_member attrib = args->material->material.attrib.m; + if (!attrib.flag_28 && (args->blend_color.w < 1.0f + || (attrib.alpha_texture || attrib.alpha_material) && !attrib.punch_through + || args->sub_mesh->attrib.m.transparent) + || attrib.punch_through) { + uniform_value[U_ALPHA_TEST] = 1; + draw_sub_mesh_translucent(rctx, args); + } + else { + uniform_value[U_ALPHA_TEST] = 0; + draw_sub_mesh_translucent(rctx, args); } } - obj_sub_mesh* sub_mesh = draw->sub_mesh; - if (sub_mesh->index_format != OBJ_INDEX_U8) - obj_sub_mesh_draw(rctx, - sub_mesh->primitive_type, - sub_mesh->num_index, - sub_mesh->first_index, - sub_mesh->last_index, - sub_mesh->index_format, - sub_mesh->index_offset); + void draw_sub_mesh_translucent(render_context* rctx, const ObjSubMeshArgs* args) { + const obj_material_data* material = args->material; + const std::vector* textures = args->textures; + if (rctx->draw_state.shader_index != -1) { + rctx->obj_batch.g_material_state_emission = args->emission; + draw_object_material_set_uniform(material, false); + if (material->material.attrib.m.alpha_texture) + uniform_value[U_TEXTURE_COUNT] = 0; + rctx->obj_batch.g_texture_blend = { (float_t)uniform_value[U_TEXTURE_BLEND], 0.0f, 0.0f, 0.0f }; + shaders_ft.set_opengl_shader(rctx->draw_state.shader_index); + } - if (tex_id != -1) - gl_state_active_bind_texture_2d(tex_index, 0); + draw_object_vertex_attrib_set_default(rctx, args); + if (args->material->material.attrib.m.double_sided) + gl_state_disable_cull_face(); - gl_state_enable_cull_face(); - draw_object_vertex_attrib_reset_default(draw); + GLuint tex_id = -1; + int32_t tex_index = 0; + if (material->material.attrib.m.alpha_texture) { + const obj_material_texture_data* texdata = material->material.texdata; + uint32_t texture_id = -1; + uint32_t texture_index = -1; + if (!material->material.shader_compo.m.transparency) { + for (int32_t i = 0; i < 8; i++, texdata++) + if (texdata->shader_info.m.tex_type == OBJ_MATERIAL_TEXTURE_COLOR) { + texture_id = texdata->tex_index; + texture_index = texdata->texture_index; + break; + } + tex_index = 0; + } + else { + for (int32_t i = 0; i < 8; i++, texdata++) + if (texdata->shader_info.m.tex_type == OBJ_MATERIAL_TEXTURE_TRANSPARENCY) { + if (texdata->attrib.m.flag_29) { + texture_id = texdata->tex_index; + texture_index = texdata->texture_index; + } + break; + } + tex_index = 4; + } - if (rctx->draw_state.shader_index != -1) - uniform_value_reset(); + if (texture_id != -1) { + for (int32_t j = 0; j < args->texture_pattern_count; j++) + if (args->texture_pattern_array[j].src == ::texture_id(0, texture_id)) { + texture* tex = texture_storage_get_texture(args->texture_pattern_array[j].dst); + if (tex) + tex_id = tex->tex; + break; + } - rctx->draw_state.stats.object_translucent_draw_count++; + if (tex_id == -1) + tex_id = (*textures)[texdata->texture_index]->tex; + + if (tex_id == -1) + tex_id = 0; + + gl_state_active_bind_texture_2d(tex_index, tex_id); + } + } + + const obj_sub_mesh* sub_mesh = args->sub_mesh; + if (sub_mesh->index_format != OBJ_INDEX_U8) + draw(rctx, + sub_mesh->primitive_type, + sub_mesh->num_index, + sub_mesh->first_index, + sub_mesh->last_index, + sub_mesh->index_format, + sub_mesh->index_offset); + + if (tex_id != -1) + gl_state_active_bind_texture_2d(tex_index, 0); + + gl_state_enable_cull_face(); + draw_object_vertex_attrib_reset_default(args); + + if (rctx->draw_state.shader_index != -1) + uniform_value_reset(); + + rctx->draw_state.stats.object_translucent_draw_count++; + } } -inline void draw_object_model_mat_load(render_context* rctx, mat4* mat) { - shaders_ft.state_matrix_set_modelview(0, *mat, rctx->view_mat, true); - shaders_ft.state_matrix_set_program(6, *mat); +inline void draw_object_model_mat_load(render_context* rctx, const mat4& mat) { + rctx->obj_batch.set_transforms(mat, rctx->view_mat, rctx->proj_mat); + rctx->obj_batch.set_g_joint(mat); } -inline void model_mat_face_camera_position(mat4* view, mat4* src, mat4* dst) { +inline void model_mat_face_camera_position(const mat4* view, const mat4* src, mat4* dst) { vec3 trans; mat4_get_translation(view, &trans); mat4_mult_vec3_inv_trans(view, &trans, &trans); @@ -342,52 +408,17 @@ inline void model_mat_face_camera_position(mat4* view, mat4* src, mat4* dst) { dst->row3.w = 1.0f; } -inline void model_mat_face_camera_view(mat4* inv_view_rot, mat4* src, mat4* dst) { - mat4_mult(inv_view_rot, src, dst); -} - -void obj_sub_mesh_draw(render_context* rctx, obj_primitive_type primitive_type, - uint32_t count, uint16_t start, uint16_t end, obj_index_format index_format, size_t indices) { - GLenum mesh_draw_mode[] = { - GL_ZERO, //GL_POINTS, - GL_LINES, - GL_LINE_STRIP, - GL_LINE_LOOP, - GL_TRIANGLES, - GL_TRIANGLE_STRIP, - GL_TRIANGLE_FAN, - GL_ZERO, //GL_QUADS, - GL_ZERO, //GL_QUAD_STRIP, - GL_ZERO, //GL_POLYGON, - }; - - GLenum mesh_indices_type[] = { - GL_ZERO, - GL_UNSIGNED_SHORT, - GL_UNSIGNED_INT, - }; - - if (primitive_type == OBJ_PRIMITIVE_TRIANGLE_STRIP && index_format == OBJ_INDEX_U16) { - shaders_ft.enable_primitive_restart(); - shaders_ft.set_primitive_restart_index(0xFFFF); - shaders_ft.draw_range_elements(GL_TRIANGLE_STRIP, - start, end, count, GL_UNSIGNED_SHORT, (void*)indices); - shaders_ft.disable_primitive_restart(); - } - else - shaders_ft.draw_elements(mesh_draw_mode[primitive_type], - count, mesh_indices_type[index_format], (void*)indices); - - rctx->draw_state.stats.draw_count += count; - if (primitive_type == OBJ_PRIMITIVE_TRIANGLES) - rctx->draw_state.stats.draw_triangle_count += count / 3; - else if (primitive_type == OBJ_PRIMITIVE_TRIANGLE_STRIP) - rctx->draw_state.stats.draw_triangle_count += count - 2; +inline void model_mat_face_camera_view(const mat4* view, const mat4* src, mat4* dst) { + mat3 mat; + mat3_from_mat4(view, &mat); + mat3_inverse(&mat, &mat); + mat4_from_mat3(&mat, dst); + mat4_mult(dst, src, dst); } static bool draw_object_blend_set(render_context* rctx, - draw_object* draw, obj_material_shader_lighting_type lighting_type) { - GLenum blend_factor_table[] = { + const mdl::ObjSubMeshArgs* args, obj_material_shader_lighting_type lighting_type) { + static const GLenum blend_factor_table[] = { GL_ZERO, GL_ONE, GL_SRC_COLOR, @@ -406,14 +437,14 @@ static bool draw_object_blend_set(render_context* rctx, GL_ZERO, }; - obj_material_data* material = draw->material; + const obj_material_data* material = args->material; obj_material_attrib_member attrib = material->material.attrib.m; if ((!attrib.alpha_texture && !attrib.alpha_material) || attrib.punch_through) return false; GLenum src_blend_factor = blend_factor_table[attrib.src_blend_factor]; GLenum dst_blend_factor = blend_factor_table[attrib.dst_blend_factor]; - if (draw->chara_color) { + if (args->chara_color) { light_set* set = &rctx->light_set[LIGHT_SET_MAIN]; light_data* chara_color = &set->lights[LIGHT_CHARA_COLOR]; vec4 specular; @@ -421,7 +452,7 @@ static bool draw_object_blend_set(render_context* rctx, if (specular.w >= 4.0f && chara_color->get_type() == LIGHT_PARALLEL && (src_blend_factor != GL_ONE || dst_blend_factor)) { int32_t shader_index = rctx->draw_state.shader_index; - if (rctx->draw_state.shader_index == -1) { + if (shader_index == -1) { shader_index = material->material.shader.index; if (shader_index == SHADER_FT_BLINN) { if (lighting_type == OBJ_MATERIAL_SHADER_LIGHTING_CONSTANT) @@ -448,9 +479,9 @@ static bool draw_object_blend_set(render_context* rctx, return dst_blend_factor == GL_ONE; } -static void draw_object_chara_color_fog_set(render_context* rctx, draw_object* draw, bool disable_fog) { +static void draw_object_chara_color_fog_set(render_context* rctx, const mdl::ObjSubMeshArgs* args, bool disable_fog) { uniform_value[U_CHARA_COLOR] = 0; - if (draw->chara_color) { + if (args->chara_color) { light_set* set = &rctx->light_set[LIGHT_SET_MAIN]; light_data* chara_color = &set->lights[LIGHT_CHARA_COLOR]; vec4 specular; @@ -459,7 +490,7 @@ static void draw_object_chara_color_fog_set(render_context* rctx, draw_object* d uniform_value[U_CHARA_COLOR] = 1; } - obj_material_attrib_member attrib = draw->material->material.attrib.m; + obj_material_attrib_member attrib = args->material->material.attrib.m; if (!attrib.no_fog && !disable_fog) { if (attrib.has_fog_height) uniform_value[U_FOG_HEIGHT] = 2 + attrib.fog_height; @@ -469,7 +500,7 @@ static void draw_object_chara_color_fog_set(render_context* rctx, draw_object* d } } -static void draw_object_material_reset_default(obj_material_data* mat_data) { +static void draw_object_material_reset_default(const obj_material_data* mat_data) { if (mat_data) { gl_state_enable_cull_face(); obj_material_attrib_member attrib = mat_data->material.attrib.m; @@ -485,40 +516,40 @@ static void draw_object_material_reset_reflect() { uniform_value_reset(); } -static void draw_object_material_set_default(render_context* rctx, draw_object* draw, bool use_shader) { - std::vector* textures = draw->textures; - obj_material_data* material = draw->material; +static void draw_object_material_set_default(render_context* rctx, const mdl::ObjSubMeshArgs* args, bool use_shader) { + const std::vector* textures = args->textures; + const obj_material_data* material = args->material; obj_material_shader_lighting_type lighting_type = material->material.shader_info.get_lighting_type(); - bool disable_fog = draw_object_blend_set(rctx, draw, lighting_type); - draw_object_material_set_uniform(rctx, material, false); + bool disable_fog = draw_object_blend_set(rctx, args, lighting_type); + draw_object_material_set_uniform(material, false); if (!rctx->draw_state.light) uniform_value[U_LIGHT_0] = 0; - else if (draw->self_shadow) + else if (args->self_shadow) uniform_value[U_LIGHT_0] = 1; else - uniform_value[U_LIGHT_0] = draw->sub_mesh->attrib.m.recieve_shadow ? 1 : 0; + uniform_value[U_LIGHT_0] = args->sub_mesh->attrib.m.recieve_shadow ? 1 : 0; uniform_value[U_SHADOW] = 0; uniform_value[U_SELF_SHADOW] = rctx->draw_state.self_shadow ? 1 : 0; - obj_material_texture_data* texdata = material->material.texdata; - uniform_value[U_SHADOW] = draw->shadow > SHADOW_CHARA; + const obj_material_texture_data* texdata = material->material.texdata; + uniform_value[U_SHADOW] = args->shadow > SHADOW_CHARA; for (int32_t i = 0, j = 0; i < 8; i++, texdata++) { if (texdata->tex_index == -1) continue; GLuint tex_id = -1; uint32_t texture_id = texdata->tex_index; - for (int32_t j = 0; j < draw->texture_pattern_count; j++) - if (draw->texture_pattern_array[j].src == ::texture_id(0, texture_id)) { - texture* tex = texture_storage_get_texture(draw->texture_pattern_array[j].dst); + for (int32_t j = 0; j < args->texture_pattern_count; j++) + if (args->texture_pattern_array[j].src == ::texture_id(0, texture_id)) { + texture* tex = texture_storage_get_texture(args->texture_pattern_array[j].dst); if (tex) tex_id = tex->tex; break; } if (tex_id == -1) - tex_id = (*textures)[texdata->texture_index]; + tex_id = (*textures)[texdata->texture_index]->tex; if (tex_id == -1) continue; @@ -573,7 +604,7 @@ static void draw_object_material_set_default(render_context* rctx, draw_object* wrap_t = 0; texture* tex = texture_storage_get_texture(::texture_id(0, texture_id)); - gl_state_bind_sampler(tex_index, rctx->draw_pass.samplers[(wrap_t * 3 + gl_state_bind_sampler(tex_index, rctx->render_manager.samplers[(wrap_t * 3 + wrap_s) * 2 + (tex->max_mipmap_level > 0 ? 1 : 0)]); } @@ -603,28 +634,21 @@ static void draw_object_material_set_default(render_context* rctx, draw_object* uniform_value[U0B] = 1; } - vec4 ambient; - *(vec3*)&ambient = *(vec3*)&material->material.color.ambient; - if (rctx->draw_pass.sss_data.enable) - ambient.w = material->material.color.ambient.w; - else - ambient.w = 1.0f; + draw_object_chara_color_fog_set(rctx, args, disable_fog); - vec4 diffuse = material->material.color.diffuse; - vec4 emission = draw->emission; - draw_object_chara_color_fog_set(rctx, draw, disable_fog); - shaders_ft.state_material_set_ambient(false, ambient); - shaders_ft.state_material_set_diffuse(false, diffuse); - shaders_ft.state_material_set_emission(false, emission); + rctx->obj_batch.g_material_state_ambient = material->material.color.ambient; + if (!rctx->render_manager.sss_data.enable) + rctx->obj_batch.g_material_state_ambient.w = 1.0f; + rctx->obj_batch.g_material_state_diffuse = material->material.color.diffuse; + rctx->obj_batch.g_material_state_emission = args->emission; float_t line_light; if (lighting_type == OBJ_MATERIAL_SHADER_LIGHTING_PHONG) { - vec4 specular = material->material.color.specular; - shaders_ft.state_material_set_specular(false, specular); + const vec4& specular = material->material.color.specular; + rctx->obj_batch.g_material_state_specular = specular; - vec3 luma_coeff = { 0.30f, 0.59f, 0.11f }; - float_t luma = vec3::dot(*(vec3*)&specular, luma_coeff); - if (luma >= 0.0099999998f || draw->texture_color_coeff.w >= 0.1f) + float_t luma = vec3::dot(*(vec3*)&specular, { 0.30f, 0.59f, 0.11f }); + if (luma >= 0.01f || args->texture_color_coefficients.w >= 0.1f) uniform_value[U_SPECULAR_IBL] = 1; else uniform_value[U_SPECULAR_IBL] = 0; @@ -638,66 +662,66 @@ static void draw_object_material_set_default(render_context* rctx, draw_object* line_light = 0.0; if (!use_shader) - shaders_ft.set(SHADER_FT_SIMPLE); + shaders_ft.set_opengl_shader(SHADER_FT_SIMPLE); else if (rctx->draw_state.shader_index != -1) - shaders_ft.set(rctx->draw_state.shader_index); + shaders_ft.set_opengl_shader(rctx->draw_state.shader_index); else if (material->material.shader.index != -1) { if (material->material.shader.index != SHADER_FT_BLINN) - shaders_ft.set(material->material.shader.index); + shaders_ft.set_opengl_shader(material->material.shader.index); else if (lighting_type == OBJ_MATERIAL_SHADER_LIGHTING_LAMBERT) - shaders_ft.set(SHADER_FT_LAMBERT); + shaders_ft.set_opengl_shader(SHADER_FT_LAMBERT); else if (lighting_type == OBJ_MATERIAL_SHADER_LIGHTING_PHONG) - shaders_ft.set(SHADER_FT_BLINN); + shaders_ft.set_opengl_shader(SHADER_FT_BLINN); else - shaders_ft.set(SHADER_FT_CONSTANT); + shaders_ft.set_opengl_shader(SHADER_FT_CONSTANT); } else - shaders_ft.set(SHADER_FT_CONSTANT); + shaders_ft.set_opengl_shader(SHADER_FT_CONSTANT); + rctx->obj_batch.g_texture_blend = { (float_t)uniform_value[U_TEXTURE_BLEND], 0.0f, 0.0f, 0.0f }; if (lighting_type != OBJ_MATERIAL_SHADER_LIGHTING_CONSTANT) { - float_t shininess; - if (material->material.shader.index == SHADER_FT_EYEBALL) - shininess = 10.0f; + float_t state_shininess; + if (material->material.shader.index == SHADER_FT_GLASEYE/*SHADER_FT_EYEBALL*/) + state_shininess = 10.0f; else { - shininess = (material->material.color.shininess - 16.0f) * (float_t)(1.0 / 112.0); - shininess = max_def(shininess, 0.0f); + state_shininess = (material->material.color.shininess - 16.0f) * (float_t)(1.0 / 112.0); + state_shininess = max_def(state_shininess, 0.0f); } - shaders_ft.state_material_set_shininess(false, shininess, 0.0f, 0.0f, 1.0f); + rctx->obj_batch.g_material_state_shininess = { state_shininess, 0.0f, 0.0f, 1.0f }; float_t fresnel = (float_t)material->material.shader_info.m.fresnel_type; - if (fresnel > 9.0) - fresnel = 9.0; + if (fresnel > 9.0f) + fresnel = 9.0f; else if (fresnel == 0.0f) fresnel = rctx->draw_state.fresnel; - fresnel = (fresnel - 1.0f) * (0.12f * 0.82f); + fresnel = (fresnel - 1.0f) * 0.12f * 0.82f; - shaders_ft.local_vert_set(0, fresnel, 0.18f, line_light, 0.0f); - shaders_ft.local_frag_set(9, fresnel, 0.18f, line_light, 0.0f); + rctx->obj_batch.g_fresnel_coefficients = { fresnel, 0.18f, line_light, 0.0f }; - shininess = max_def(material->material.color.shininess, 1.0f); - shaders_ft.local_vert_set(1, shininess, 0.0f, 0.0f, 0.0f); - shaders_ft.local_frag_set(3, shininess, 0.0f, 0.0f, 0.0f); + float_t shininess = max_def(material->material.color.shininess, 1.0f); + rctx->obj_batch.g_shininess = { shininess, 0.0f, 0.0f, 0.0f }; switch (material->material.shader.index) { case SHADER_FT_SKIN: { - vec4 texture_color_coeff = draw->texture_color_coeff; - vec4 texture_color_offset = draw->texture_color_offset; - vec4 texture_specular_coeff = draw->texture_specular_coeff; - vec4 texture_specular_offset = draw->texture_specular_offset; + vec4 texture_color_coefficients = args->texture_color_coefficients; + vec4 texture_color_offset = args->texture_color_offset; + vec4 texture_specular_coefficients = args->texture_specular_coefficients; + vec4 texture_specular_offset = args->texture_specular_offset; - texture_color_coeff.w *= 0.015f; - texture_specular_coeff.w *= 0.015f; + texture_color_coefficients.w *= 0.015f; + texture_specular_coefficients.w *= 0.015f; - shaders_ft.local_frag_set(5, texture_color_coeff); - shaders_ft.local_frag_set(6, texture_color_offset); - shaders_ft.local_frag_set(7, texture_specular_coeff); - shaders_ft.local_frag_set(8, texture_specular_offset); + rctx->obj_batch.g_texture_color_coefficients = texture_color_coefficients; + rctx->obj_batch.g_texture_color_offset = texture_color_offset; + rctx->obj_batch.g_texture_specular_coefficients = texture_specular_coefficients; + rctx->obj_batch.g_texture_specular_offset = texture_specular_offset; } break; case SHADER_FT_HAIR: case SHADER_FT_CLOTH: case SHADER_FT_TIGHTS: - shaders_ft.local_frag_set(5, 1.0f - draw->texture_color_coeff.w * 0.4f, - 0.0f, 0.0f, draw->texture_color_coeff.w * 0.02f); + rctx->obj_batch.g_texture_color_coefficients = { + 1.0f - args->texture_color_coefficients.w * 0.4f, + 0.0f, 0.0f, args->texture_color_coefficients.w * 0.02f }; break; } } @@ -705,7 +729,7 @@ static void draw_object_material_set_default(render_context* rctx, draw_object* draw_object_material_set_parameter(rctx, material); } -static void draw_object_material_set_parameter(render_context* rctx, obj_material_data* mat_data) { +static void draw_object_material_set_parameter(render_context* rctx, const obj_material_data* mat_data) { float_t bump_depth; float_t intensity; float_t reflect_uv_scale; @@ -720,7 +744,7 @@ static void draw_object_material_set_parameter(render_context* rctx, obj_materia vec4 specular = mat_data->material.color.specular; specular.w = rctx->draw_state.reflectivity; - shaders_ft.state_material_set_specular(false, specular); + rctx->obj_batch.g_material_state_specular = specular; } else { bump_depth = mat_data->material.bump_depth; @@ -730,44 +754,35 @@ static void draw_object_material_set_parameter(render_context* rctx, obj_materia inv_bump_depth = (1.0f - bump_depth) * 256.0f + 1.0f; } - intensity = max_def(intensity, 1.0f); - - shaders_ft.local_vert_set(2, inv_bump_depth, bump_depth, 0.0f, 0.0f); - shaders_ft.local_frag_set(0, inv_bump_depth, bump_depth, 0.0f, 0.0f); - - shaders_ft.local_frag_set(1, intensity, intensity, intensity * 25.5f, 1.0f); - shaders_ft.local_frag_set(2, - reflect_uv_scale, reflect_uv_scale, refract_uv_scale, refract_uv_scale); - - vec4 specular; - shaders_ft.env_vert_get(17, specular); - *(vec3*)&specular = *(vec3*)&specular * *(vec3*)&mat_data->material.color.specular; - shaders_ft.env_vert_set(18, specular); + rctx->obj_batch.g_bump_depth = { inv_bump_depth, bump_depth, 0.0f, 0.0f }; + rctx->obj_batch.g_intensity = { intensity, max_def(intensity, 1.0f), intensity * 25.5f, 1.0f }; + rctx->obj_batch.g_reflect_uv_scale = { + reflect_uv_scale, reflect_uv_scale, refract_uv_scale, refract_uv_scale }; } -static void draw_object_material_set_reflect(render_context* rctx, draw_object* draw) { - obj_material_data* material = draw->material; - std::vector* textures = draw->textures; +static void draw_object_material_set_reflect(render_context* rctx, const mdl::ObjSubMeshArgs* args) { + const obj_material_data* material = args->material; + const std::vector* textures = args->textures; if (material->material.attrib.m.double_sided) gl_state_disable_cull_face(); - obj_material_texture_data* texdata = material->material.texdata; + const obj_material_texture_data* texdata = material->material.texdata; for (int32_t i = 0; i < 1; i++, texdata++) { uint32_t texture_id = texdata->tex_index; if (texdata->tex_index == -1) break; GLuint tex_id = -1; - for (int32_t j = 0; j < draw->texture_pattern_count; j++) - if (draw->texture_pattern_array[j].src == ::texture_id(0, texture_id)) { - texture* tex = texture_storage_get_texture(draw->texture_pattern_array[j].dst); + for (int32_t j = 0; j < args->texture_pattern_count; j++) + if (args->texture_pattern_array[j].src == ::texture_id(0, texture_id)) { + texture* tex = texture_storage_get_texture(args->texture_pattern_array[j].dst); if (tex) tex_id = tex->tex; break; } if (tex_id == -1) - tex_id = (*textures)[texdata->texture_index]; + tex_id = (*textures)[texdata->texture_index]->tex; if (tex_id == -1) tex_id = 0; @@ -791,7 +806,7 @@ static void draw_object_material_set_reflect(render_context* rctx, draw_object* wrap_t = 0; texture* tex = texture_storage_get_texture(::texture_id(0, texture_id)); - gl_state_bind_sampler(i, rctx->draw_pass.samplers[(wrap_t * 3 + gl_state_bind_sampler(i, rctx->render_manager.samplers[(wrap_t * 3 + wrap_s) * 2 + (tex->max_mipmap_level > 0 ? 1 : 0)]); } @@ -808,20 +823,21 @@ static void draw_object_material_set_reflect(render_context* rctx, draw_object* else { ambient = material->material.color.ambient; diffuse = material->material.color.diffuse; - emission = draw->emission; + emission = args->emission; specular = material->material.color.specular; } - draw_object_material_set_uniform(rctx, material, true); - shaders_ft.set(rctx->draw_state.shader_index); - shaders_ft.state_material_set_ambient(false, ambient); - shaders_ft.state_material_set_diffuse(false, diffuse); - shaders_ft.state_material_set_emission(false, emission); - shaders_ft.state_material_set_specular(false, specular); - draw_object_material_set_parameter(rctx, draw->material); + draw_object_material_set_uniform(material, true); + rctx->obj_batch.g_texture_blend = { (float_t)uniform_value[U_TEXTURE_BLEND], 0.0f, 0.0f, 0.0f }; + shaders_ft.set_opengl_shader(rctx->draw_state.shader_index); + rctx->obj_batch.g_material_state_ambient = ambient; + rctx->obj_batch.g_material_state_diffuse = diffuse; + rctx->obj_batch.g_material_state_emission = emission; + rctx->obj_batch.g_material_state_specular = specular; + draw_object_material_set_parameter(rctx, args->material); } -static void draw_object_material_set_uniform(render_context* rctx, obj_material_data* mat_data, bool disable_color_l) { +static void draw_object_material_set_uniform(const obj_material_data* mat_data, bool disable_color_l) { obj_shader_compo_member shader_compo = mat_data->material.shader_compo.m; obj_material_shader_attrib_member shader_info = mat_data->material.shader_info.m; @@ -874,48 +890,45 @@ static void draw_object_material_set_uniform(render_context* rctx, obj_material_ uniform_value[U45] = 1; } -static void draw_object_vertex_attrib_reset_default(draw_object* draw) { - obj_mesh* mesh = draw->mesh; +static void draw_object_vertex_attrib_reset_default(const mdl::ObjSubMeshArgs* args) { + const obj_mesh* mesh = args->mesh; obj_vertex_format vertex_format = mesh->vertex_format; if (vertex_format & OBJ_VERTEX_BONE_DATA) uniform_value[U_BONE_MAT] = 0; - if (draw->morph_array_buffer) { + if (args->morph_array_buffer) { uniform_value[U_MORPH] = 0; uniform_value[U_MORPH_COLOR] = 0; } - uniform_value[U_INSTANCE] = 0; - shaders_ft.state_matrix_set_texture(0, mat4_identity); - shaders_ft.state_matrix_set_texture(1, mat4_identity); + /*if (args->instances_count) + uniform_value[U_INSTANCE] = 0;*/ + gl_state_active_texture(0); } -static void draw_object_vertex_attrib_reset_reflect(draw_object* draw) { - obj_mesh* mesh = draw->mesh; +static void draw_object_vertex_attrib_reset_reflect(const mdl::ObjSubMeshArgs* args) { + const obj_mesh* mesh = args->mesh; obj_vertex_format vertex_format = mesh->vertex_format; if (vertex_format & OBJ_VERTEX_BONE_DATA) uniform_value[U_BONE_MAT] = 0; - if (draw->morph_array_buffer) { + if (args->morph_array_buffer) { uniform_value[U_MORPH] = 0; uniform_value[U_MORPH_COLOR] = 0; } - shaders_ft.state_matrix_set_texture(0, mat4_identity); gl_state_active_texture(0); } -static void draw_object_vertex_attrib_set_default(draw_object* draw) { - obj_mesh* mesh = draw->mesh; - obj_sub_mesh* sub_mesh = draw->sub_mesh; - GLsizei size_vertex = (GLsizei)mesh->size_vertex; +static void draw_object_vertex_attrib_set_default(render_context* rctx, const mdl::ObjSubMeshArgs* args) { + const obj_mesh* mesh = args->mesh; obj_vertex_format vertex_format = mesh->vertex_format; bool texcoord_mat_set[4] = { false }; - obj_material_data* material = draw->material; + const obj_material_data* material = args->material; for (int32_t i = 0, j = 0, l = 0; i < 4; i++) { if (material->material.texdata[i].tex_index == -1) continue; @@ -935,13 +948,13 @@ static void draw_object_vertex_attrib_set_default(draw_object* draw) { int32_t texture_id = material->material.texdata[i].tex_index; - shaders_ft.state_matrix_set_texture(texcoord_index, + rctx->obj_batch.set_g_texcoord_transforms(texcoord_index, material->material.texdata[i].tex_coord_mat); if (material->material.texdata[i].shader_info.m.tex_type == OBJ_MATERIAL_TEXTURE_COLOR) - for (int32_t k = 0; k < draw->texture_transform_count; k++) - if (draw->texture_transform_array[k].id == texture_id) { - shaders_ft.state_matrix_set_texture(texcoord_index, - draw->texture_transform_array[k].mat); + for (int32_t k = 0; k < args->texture_transform_count; k++) + if (args->texture_transform_array[k].id == texture_id) { + rctx->obj_batch.set_g_texcoord_transforms(texcoord_index, + args->texture_transform_array[k].mat); texcoord_mat_set[texcoord_index] = true; break; } @@ -952,7 +965,7 @@ static void draw_object_vertex_attrib_set_default(draw_object* draw) { else uniform_value[U_BONE_MAT] = 0; - if (draw->morph_array_buffer) { + if (args->morph_array_buffer) { uniform_value[U_MORPH] = 1; if (vertex_format & OBJ_VERTEX_COLOR0) @@ -960,38 +973,34 @@ static void draw_object_vertex_attrib_set_default(draw_object* draw) { else uniform_value[U_MORPH_COLOR] = 0; - shaders_ft.env_vert_set(13, draw->morph_value, 1.0f - draw->morph_value, 0.0f, 0.0f); + rctx->obj_batch.g_morph_weight = { args->morph_weight, 1.0f - args->morph_weight, 0.0f, 0.0f }; } else { uniform_value[U_MORPH] = 0; uniform_value[U_MORPH_COLOR] = 0; - - shaders_ft.env_vert_set(13, 0.0f, 1.0f, 0.0f, 0.0f); } - if (draw->instances_count) + /*if (args->instances_count) uniform_value[U_INSTANCE] = 1; else - uniform_value[U_INSTANCE] = 0; + uniform_value[U_INSTANCE] = 0;*/ } -static void draw_object_vertex_attrib_set_reflect(draw_object* draw) { - obj_mesh* mesh = draw->mesh; - obj_sub_mesh* sub_mesh = draw->sub_mesh; - GLsizei size_vertex = (GLsizei)mesh->size_vertex; +static void draw_object_vertex_attrib_set_reflect(render_context* rctx, const mdl::ObjSubMeshArgs* args) { + const obj_mesh* mesh = args->mesh; obj_vertex_format vertex_format = mesh->vertex_format; - obj_material_data* material = draw->material; + const obj_material_data* material = args->material; if (material->material.texdata[0].tex_index != -1) { int32_t tex_index = material->material.texdata[0].tex_index; - shaders_ft.state_matrix_set_texture(0, + rctx->obj_batch.set_g_texcoord_transforms(0, material->material.texdata[0].tex_coord_mat); if (material->material.texdata[0].shader_info.m.tex_type == OBJ_MATERIAL_TEXTURE_COLOR) - for (int32_t j = 0; j < draw->texture_transform_count; j++) - if (draw->texture_transform_array[j].id == tex_index) { - shaders_ft.state_matrix_set_texture(0, - draw->texture_transform_array[j].mat); + for (int32_t j = 0; j < args->texture_transform_count; j++) + if (args->texture_transform_array[j].id == tex_index) { + rctx->obj_batch.set_g_texcoord_transforms(0, + args->texture_transform_array[j].mat); break; } } @@ -1001,7 +1010,7 @@ static void draw_object_vertex_attrib_set_reflect(draw_object* draw) { else uniform_value[U_BONE_MAT] = 0; - if (draw->morph_array_buffer) { + if (args->morph_array_buffer) { uniform_value[U_MORPH] = 1; if (vertex_format & OBJ_VERTEX_COLOR0) @@ -1009,12 +1018,10 @@ static void draw_object_vertex_attrib_set_reflect(draw_object* draw) { else uniform_value[U_MORPH_COLOR] = 0; - shaders_ft.env_vert_set(13, draw->morph_value, 1.0f - draw->morph_value, 0.0f, 0.0f); + rctx->obj_batch.g_morph_weight = { args->morph_weight, 1.0f - args->morph_weight, 0.0f, 0.0f }; } else { uniform_value[U_MORPH] = 0; uniform_value[U_MORPH_COLOR] = 0; - - shaders_ft.env_vert_set(13, 0.0f, 1.0f, 0.0f, 0.0f); } } diff --git a/src/CRE/draw_object.hpp b/src/CRE/draw_object.hpp index b527512a..e18c16c6 100644 --- a/src/CRE/draw_object.hpp +++ b/src/CRE/draw_object.hpp @@ -11,26 +11,23 @@ #include "object.hpp" #include "render_context.hpp" -struct texture_data_struct { - int32_t field_0; - vec3 texture_color_coeff; - vec3 texture_color_offset; - vec3 texture_specular_coeff; - vec3 texture_specular_offset; - - texture_data_struct(); +namespace mdl { + extern void draw(render_context* rctx, obj_primitive_type primitive_type, uint32_t count, + uint16_t start, uint16_t end, obj_index_format index_format, size_t indices); + extern void draw_sub_mesh(render_context* rctx, const ObjSubMeshArgs* args, const mat4* model, + void(*func)(render_context* rctx, const ObjSubMeshArgs* args)); + /*extern void draw_sub_mesh_show_vector(render_context* rctx, + const ObjSubMeshArgs* args, const mat4* model, int32_t show_vector);*/ + extern void draw_sub_mesh_default(render_context* rctx, const ObjSubMeshArgs* args); + extern void draw_sub_mesh_default_instanced(render_context* rctx, + const ObjSubMeshArgs* args, const mat4* mat); + extern void draw_sub_mesh_sss(render_context* rctx, const ObjSubMeshArgs* args); + extern void draw_sub_mesh_reflect(render_context* rctx, const ObjSubMeshArgs* args); + extern void draw_sub_mesh_reflect_reflect_map(render_context* rctx, const ObjSubMeshArgs* args); + extern void draw_sub_mesh_shadow(render_context* rctx, const ObjSubMeshArgs* args); + extern void draw_sub_mesh_translucent(render_context* rctx, const ObjSubMeshArgs* args); }; -extern void draw_object_draw(render_context* rctx, draw_object* draw, mat4* model, - void(*draw_object_func)(render_context* rctx, draw_object* draw), int32_t show_vector); -extern void draw_object_draw_default(render_context* rctx, draw_object* draw); -extern void draw_object_draw_sss(render_context* rctx, draw_object* draw); -extern void draw_object_draw_reflect(render_context* rctx, draw_object* draw); -extern void draw_object_draw_reflect_reflect_map(render_context* rctx, draw_object* draw); -extern void draw_object_draw_shadow(render_context* rctx, draw_object* draw); -extern void draw_object_draw_translucent(render_context* rctx, draw_object* draw); -extern void draw_object_model_mat_load(render_context* rctx, mat4* mat); -extern void model_mat_face_camera_position(mat4* view, mat4* src, mat4* dst); -extern void model_mat_face_camera_view(mat4* view, mat4* src, mat4* dst); -extern void obj_sub_mesh_draw(render_context* rctx, obj_primitive_type primitive_type, - uint32_t count, uint16_t start, uint16_t end, obj_index_format index_format, size_t indices); +extern void draw_object_model_mat_load(render_context* rctx, const mat4& mat); +extern void model_mat_face_camera_position(const mat4* view, const mat4* src, mat4* dst); +extern void model_mat_face_camera_view(const mat4* view, const mat4* src, mat4* dst); diff --git a/src/CRE/draw_pass.cpp b/src/CRE/draw_pass.cpp deleted file mode 100644 index 196e5a28..00000000 --- a/src/CRE/draw_pass.cpp +++ /dev/null @@ -1,1436 +0,0 @@ -/* - by korenkonder - GitHub/GitLab: korenkonder -*/ - -#include "draw_pass.hpp" -#include "Glitter/glitter.hpp" -#include "light_param/fog.hpp" -#include "light_param/light.hpp" -#include "rob/rob.hpp" -#include "clear_color.hpp" -#include "gl_state.hpp" -#include "post_process.hpp" -#include "render_texture.hpp" -#include "shader_ft.hpp" -#include "shader_glsl.hpp" -#include "static_var.hpp" -#include "task_effect.hpp" -#include "texture.hpp" - -struct texture_param { - GLint width; - GLint height; -}; - -static void blur_filter_apply(GLuint tex_0, GLuint tex_1, blur_filter_mode filter); -static void draw_pass_begin(draw_pass* a1); -static void draw_pass_shadow(render_context* rctx, draw_pass* a1); -static void draw_pass_shadow_filter(render_texture* a1, render_texture* a2, - render_texture* a3, float_t sigma, float_t far_texel_offset, bool enable_lit_proj); -static bool draw_pass_shadow_litproj(render_context* rctx, light_proj* litproj); -static void draw_pass_sss(render_context* rctx, draw_pass* a1); -static void draw_pass_sss_contour(render_context* rctx, post_process* pp); -static void draw_pass_sss_filter(render_context* rctx, sss_data* a1); -static void draw_pass_reflect(render_context* rctx, draw_pass* a1); -static void draw_pass_refract(render_context* rctx, draw_pass* a1); -static void draw_pass_preprocess(render_context* rctx, draw_pass* a1); -static void draw_pass_clear(render_context* rctx, draw_pass* a1); -static void draw_pass_pre_sprite(render_context* rctx, draw_pass* a1); -static void draw_pass_3d(render_context* rctx, draw_pass* a1); -static void draw_pass_3d_contour(render_context* rctx, draw_pass* a1); -static int32_t draw_pass_3d_get_translucent_count(render_context* rctx); -static void draw_pass_3d_shadow_reset(render_context* rctx); -static void draw_pass_3d_shadow_set(shadow* shad, render_context* rctx); -static void draw_pass_show_vector(render_context* rctx, draw_pass* a1); -static void draw_pass_post_process(render_context* rctx, draw_pass* a1); -static void draw_pass_sprite(render_context* rctx, draw_pass* a1); -static void draw_pass_end(draw_pass* pass, draw_pass_type type); -static void draw_pass_3d_grid(render_context* rctx); -static void draw_pass_set_camera(camera* cam); -static void texture_params_get(GLuint tex_0, texture_param* tex_0_param, - GLuint tex_1, texture_param* tex_1_param, GLuint tex_2, texture_param* tex_2_param); -static void texture_params_restore(texture_param* tex_0_param, - texture_param* tex_1_param, texture_param* tex_2_param); - -extern bool draw_grid_3d; -extern shader_glsl* grid_shader; -extern GLuint grid_vao; -extern size_t grid_vertex_count; -extern light_param_data_storage* light_param_data_storage_data; - -void draw_pass_main(render_context* rctx) { - static const int32_t ibl_texture_index[] = { - 9, 10, 11, 12, 13 - }; - - camera* cam = rctx->camera; - - for (int32_t i = 0; i < 5; i++) - gl_state_active_bind_texture_cube_map(ibl_texture_index[i], - light_param_data_storage_data->textures[i]); - - shaders_ft.env_frag_set(0, - 1.0f / (float_t)rctx->post_process.render_width, - 1.0f / (float_t)rctx->post_process.render_height, - (float_t)rctx->post_process.render_width, - (float_t)rctx->post_process.render_height); - - gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - gl_state_enable_depth_test(); - gl_state_set_depth_func(GL_LEQUAL); - gl_state_set_depth_mask(GL_TRUE); - for (int32_t i = DRAW_PASS_SHADOW; i < DRAW_PASS_MAX; i++) { - rctx->draw_pass.cpu_time[i] = 0.0; - rctx->draw_pass.gpu_time[i] = 0.0; - if (!rctx->draw_pass.enable[i]) { - glGetError(); - continue; - } - - draw_pass_begin(&rctx->draw_pass); - switch (i) { - case DRAW_PASS_SHADOW: - draw_pass_shadow(rctx, &rctx->draw_pass); - break; - case DRAW_PASS_SS_SSS: - draw_pass_sss(rctx, &rctx->draw_pass); - break; - case DRAW_PASS_REFLECT: - draw_pass_reflect(rctx, &rctx->draw_pass); - break; - case DRAW_PASS_REFRACT: - draw_pass_refract(rctx, &rctx->draw_pass); - break; - case DRAW_PASS_PRE_PROCESS: - draw_pass_preprocess(rctx, &rctx->draw_pass); - break; - case DRAW_PASS_CLEAR: - draw_pass_clear(rctx, &rctx->draw_pass); - break; - case DRAW_PASS_PRE_SPRITE: - draw_pass_pre_sprite(rctx, &rctx->draw_pass); - break; - case DRAW_PASS_3D: - draw_pass_3d(rctx, &rctx->draw_pass); - break; - case DRAW_PASS_SHOW_VECTOR: - draw_pass_show_vector(rctx, &rctx->draw_pass); - break; - case DRAW_PASS_POST_PROCESS: - draw_pass_post_process(rctx, &rctx->draw_pass); - break; - case DRAW_PASS_SPRITE: - draw_pass_sprite(rctx, &rctx->draw_pass); - break; - } - draw_pass_end(&rctx->draw_pass, (draw_pass_type)i); - glGetError(); - } - rctx->object_data.check_vertex_arrays(); - gl_state_bind_vertex_array(0); - gl_state_disable_primitive_restart(); -} - -inline static void draw_pass_begin(draw_pass* a1) { - if (a1->wait_for_gpu) - glFinish(); - a1->time.get_timestamp(); -} - -static void draw_pass_shadow(render_context* rctx, draw_pass* a1) { - rctx->camera->update_data(); - rctx->view_mat = rctx->camera->view; - if (rctx->litproj->set(rctx)) { - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_OPAQUE); - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_TRANSPARENT); - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_TRANSLUCENT); - - rctx->draw_state.shader_index = -1; - uniform_value[U0A] = 0; - draw_pass_shadow_filter(&rctx->litproj->shadow_texture[0], - &rctx->litproj->shadow_texture[1], 0, 1.5f, 0.01f, true); - - if (draw_pass_shadow_litproj(rctx, rctx->litproj)) { - draw_pass_set_camera(rctx->camera); - for (int32_t i = LIGHT_SET_MAIN; i < LIGHT_SET_MAX; i++) - rctx->light_set[i].data_set(rctx->face, (light_set_id)i); - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_OPAQUE); - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_TRANSPARENT); - draw_task_sort(rctx, DRAW_OBJECT_TRANSLUCENT, 1); - gl_state_set_blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - gl_state_enable_blend(); - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_TRANSLUCENT); - gl_state_disable_blend(); - rctx->draw_state.shader_index = -1; - gl_state_bind_framebuffer(0); - } - } - - bool v3 = false; - int32_t v10[2]; - for (int32_t i = 0; i < 2; i++) { - v10[i] = draw_task_get_count(rctx, (draw_object_type)(DRAW_OBJECT_SHADOW_CHARA + i)); - if (v10[i]) - v3 = true; - } - - shadow* shad = a1->shadow_ptr; - if (a1->shadow && v3) { - int32_t v11[2]; - v11[0] = shad->field_200[0]; - v11[1] = shad->field_200[1]; - shad->field_158[0] = &shad->field_8[0]; - shad->field_158[1] = &shad->field_8[1]; - shad->field_158[2] = &shad->field_8[2]; - - int32_t v8 = DRAW_OBJECT_SHADOW_OBJECT_CHARA; - for (int32_t i = 0, j = 0; i < 2; i++, v8++) { - if (!v10[i]) - continue; - - int32_t index = v11[i]; - - texture* tex = shad->field_158[0]->color_texture; - shad->field_158[0]->bind(); - glViewport(0, 0, tex->width, tex->height); - glScissor(0, 0, tex->width, tex->height); - gl_state_enable_depth_test(); - gl_state_enable_scissor_test(); - glClearColor(1.0f, 1.0f, 1.0f, 1.0f); - glClearDepthf(1.0f); - if (!j) - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - else if (shad->field_2F5) - glClear(GL_COLOR_BUFFER_BIT); - glScissor(1, 1, tex->width - 2, tex->height - 2); - - float_t v15 = 0.0f; - float_t v16 = shad->view_region * shad->range; - vec3* interest; - vec3* view_point; - if (shad->field_2F5) { - v15 = -0.5f; - if (index) - v15 = 0.5f; - - interest = &shad->interest[index]; - view_point = &shad->view_point[index]; - } - else { - interest = &shad->interest_shared; - view_point = &shad->view_point_shared; - } - - mat4 temp; - mat4_translate(v15, 0.0f, 0.0f, &temp); - - mat4 proj; - mat4_ortho(-v16, v16, -v16, v16, shad->z_near, shad->z_far, &proj); - mat4_mult(&proj, &temp, &proj); - shaders_ft.state_matrix_set_projection(proj, false); - - mat4_look_at(view_point, interest, &rctx->view_mat); - - rctx->draw_state.shader_index = SHADER_FT_SIL; - uniform_value[U0A] = 0; - - draw_task_draw_objects_by_type(rctx, (draw_object_type)(DRAW_OBJECT_SHADOW_CHARA + v11[i])); - if (draw_task_get_count(rctx, (draw_object_type)(DRAW_OBJECT_SHADOW_OBJECT_CHARA + v11[i])) > 0) { - glColorMask(a1->field_2F8 != 0 ? GL_TRUE : GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - draw_task_draw_objects_by_type(rctx, (draw_object_type)v8); - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - } - gl_state_disable_depth_test(); - gl_state_disable_scissor_test(); - - rctx->draw_state.shader_index = -1; - if (j == shad->field_2EC - 1) { - draw_pass_shadow_filter(&shad->field_8[3], &shad->field_8[4], shad->field_158[0], - shad->field_2DC, shad->field_2E0 / (shad->field_208 * 2.0f), false); - - GLuint v5 = shad->field_8[5].color_texture->tex; - GLuint v6 = shad->field_8[6].color_texture->tex; - GLuint v7 = shad->field_8[3].color_texture->tex; - if (v5 && v6 && v7) { - texture_param tex_params[3]; - texture_params_get(v5, &tex_params[0], v7, &tex_params[1], v6, &tex_params[2]); - shad->field_8[6].bind(); - //uniform_value[U_ESM_FILTER] = 0; - //shaders_ft.set(SHADER_FT_ESMFILT); - shaders_ft.set(SHADER_FT_ESMFILT_MIN); - shaders_ft.local_frag_set(0, 1.0f / (float_t)tex_params[1].width, - 1.0f / (float_t)tex_params[1].height, 0.0f, 0.0f); - gl_state_active_bind_texture_2d(0, v7); - shaders_ft.state_matrix_set_mvp(mat4_identity); - render_texture::draw_custom(&shaders_ft); - - shad->field_8[5].bind(); - //uniform_value[U_ESM_FILTER] = 1; - //shaders_ft.set(SHADER_FT_ESMFILT); - shaders_ft.set(SHADER_FT_ESMFILT_EROSION); - shaders_ft.local_frag_set(0, 0.75f / (float_t)tex_params[2].width, - 0.75f / (float_t)tex_params[2].height, 0.0f, 0.0f); - gl_state_active_bind_texture_2d(0, v6); - shaders_ft.state_matrix_set_mvp(mat4_identity); - render_texture::draw_custom(&shaders_ft); - texture_params_restore(&tex_params[0], &tex_params[1], &tex_params[2]); - } - } - - render_texture* rend_tex = shad->field_158[1 + index]; - rend_tex->bind(); - - GLuint v11 = shad->field_158[1 + index]->color_texture->tex; - GLuint v12 = shad->field_158[0]->color_texture->tex; - if (v11 && v12) { - texture_param tex_params[3]; - texture_params_get(v11, &tex_params[0], v12, &tex_params[1], 0, 0); - uniform_value[U_IMAGE_FILTER] = 5; - shaders_ft.set(SHADER_FT_IMGFILT); - shaders_ft.local_frag_set(0, 1.0f); - shaders_ft.state_matrix_set_texture(0, mat4_identity); - shaders_ft.state_matrix_set_texture(1, mat4_identity); - shaders_ft.state_matrix_set_texture(2, mat4_identity); - shaders_ft.state_matrix_set_texture(3, mat4_identity); - shaders_ft.state_matrix_set_texture(4, mat4_identity); - shaders_ft.state_matrix_set_texture(5, mat4_identity); - shaders_ft.state_matrix_set_texture(6, mat4_identity); - shaders_ft.state_matrix_set_texture(7, mat4_identity); - gl_state_active_bind_texture_2d(0, v12); - shaders_ft.state_matrix_set_mvp(mat4_identity); - render_texture::draw_custom(&shaders_ft); - texture_params_restore(&tex_params[0], &tex_params[1], 0); - } - - rend_tex->bind(); - if (shad->blur_filter_enable[index]) - for (int32_t i = 0; i < shad->near_blur; i++) - blur_filter_apply(rend_tex->color_texture->tex, - rend_tex->color_texture->tex, shad->blur_filter); - else { - glClearColor(1.0f, 1.0f, 1.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - } - - gl_state_bind_framebuffer(0); - gl_state_bind_texture_2d(rend_tex->color_texture->tex); - glGenerateMipmap(GL_TEXTURE_2D); - gl_state_bind_texture_2d(0); - j++; - } - } - else { - shad->field_158[0] = &shad->field_8[0]; - shad->field_158[1] = &shad->field_8[1]; - shad->field_158[2] = &shad->field_8[2]; - - for (int32_t i = 1; i < 3; i++) - for (int32_t j = 0; j < 4; j++) { - shad->field_158[i]->bind(j); - glClearColor(1.0f, 1.0f, 1.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - } - } - gl_state_bind_framebuffer(0); -} - -static void draw_pass_shadow_filter(render_texture* a1, render_texture* a2, - render_texture* a3, float_t sigma, float_t far_texel_offset, bool enable_lit_proj) { - GLuint v7 = a1->color_texture->tex; - GLuint v9 = a2->color_texture->tex; - GLuint v11 = v7; - if (a3) - v11 = a3->depth_texture->tex; - - if (!v7 || !v9 || !v11) - return; - - texture_param tex_params[2]; - texture_params_get(v7, &tex_params[0], v9, &tex_params[1], 0, 0); - if (tex_params[0].width != tex_params[1].width - || tex_params[0].height != tex_params[1].height) - return; - - vec4 v15[2]; - double_t v6 = 1.0 / (sqrt(M_PI * 2.0) * sigma); - double_t v8 = -1.0 / (2.0 * sigma * sigma); - for (int32_t i = 0; i < 8; i++) - ((float_t*)v15)[i] = (float_t)(exp((double_t)((ssize_t)i * i) * v8) * v6); - - a2->bind(); - uniform_value[U_LIGHT_PROJ] = enable_lit_proj ? 1 : 0; - shaders_ft.set(SHADER_FT_ESMGAUSS); - shaders_ft.local_frag_set(0, 1.0f / (float_t)tex_params[0].width, 0.0f, 0.0f, 0.0f); - shaders_ft.local_frag_set(1, v15[0]); - shaders_ft.local_frag_set(2, v15[1]); - shaders_ft.local_frag_set(3, far_texel_offset, far_texel_offset, 0.0f, 0.0f); - - gl_state_active_bind_texture_2d(0, v11); - if (a3) { - GLint swizzle[] = { GL_RED, GL_RED, GL_RED, GL_ONE }; - glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle); - } - else { - GLint swizzle[] = { GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA }; - glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle); - } - shaders_ft.state_matrix_set_mvp(mat4_identity); - render_texture::draw_custom(&shaders_ft); - - a1->bind(); - shaders_ft.local_frag_set(0, 0.0f, 1.0f / (float_t)tex_params[0].height, 0.0f, 0.0f); - shaders_ft.local_frag_set(1, v15[0]); - shaders_ft.local_frag_set(2, v15[1]); - shaders_ft.local_frag_set(3, far_texel_offset, far_texel_offset, 0.0f, 0.0f); - gl_state_active_bind_texture_2d(0, v9); - shaders_ft.state_matrix_set_mvp(mat4_identity); - render_texture::draw_custom(&shaders_ft); - shader::unbind(); - texture_params_restore(&tex_params[0], &tex_params[1], 0); -} - -static bool draw_pass_shadow_litproj(render_context* rctx, light_proj* litproj) { - if (!litproj) - return false; - - texture* tex = texture_storage_get_texture(litproj->texture_id); - if (!tex) - return false; - - litproj->draw_texture.bind(); - - glViewport(0, 0, litproj->draw_texture.color_texture->width, - litproj->draw_texture.color_texture->height); - gl_state_enable_depth_test(); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClearDepthf(1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - if (!light_proj::set_mat(rctx, true)) { - gl_state_bind_framebuffer(0); - return false; - } - - static const vec4 border_color = 0.0f; - rctx->draw_state.shader_index = SHADER_FT_LITPROJ; - gl_state_active_bind_texture_2d(17, tex->tex); - glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, (GLfloat*)&border_color); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - gl_state_active_bind_texture_2d(18, litproj->shadow_texture[0].color_texture->tex); - glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, (GLfloat*)&border_color); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); - gl_state_active_texture(0); - return true; -} - -static void draw_pass_sss(render_context* rctx, draw_pass* a1) { - sss_data* sss = &a1->sss_data; - if (!sss->init) - return; - - if (!sss->enable) { - shaders_ft.env_frag_set(25, 0.0f, 0.0f, 0.0f, 0.0f); - return; - } - - rctx->camera->update_data(); - rctx->view_mat = rctx->camera->view; - post_process* pp = &rctx->post_process; - //if (pp->render_width > 1280.0) - // sss->npr_contour = false; - - if (sss->npr_contour) { - pp->rend_texture.bind(); - glViewport(0, 0, pp->render_width, pp->render_height); - } - else { - sss->textures[0].bind(); - glViewport(0, 0, 640, 360); - } - - glClearColor(sss->param.x, sss->param.y, sss->param.z, 0.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - draw_pass_set_camera(rctx->camera); - for (int32_t i = LIGHT_SET_MAIN; i < LIGHT_SET_MAX; i++) - rctx->light_set[i].data_set(rctx->face, (light_set_id)i); - - if (a1->shadow) - draw_pass_3d_shadow_set(a1->shadow_ptr, rctx); - else - draw_pass_3d_shadow_reset(rctx); - - rctx->draw_state.shader_index = SHADER_FT_SSS_SKIN; - gl_state_enable_depth_test(); - gl_state_set_depth_func(GL_LEQUAL); - gl_state_set_depth_mask(GL_TRUE); - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_SSS); - gl_state_disable_depth_test(); - rctx->draw_state.shader_index = -1; - draw_pass_3d_shadow_reset(rctx); - uniform_value[U_NPR] = 0; - - if (a1->npr_param == 1) { - if (sss->enable && sss->npr_contour) { - uniform_value[U_NPR] = 1; - draw_pass_sss_contour(rctx, pp); - } - else if (a1->npr) { - pp->rend_texture.bind(); - glViewport(0, 0, pp->render_width, pp->render_height); - glClear(GL_DEPTH_BUFFER_BIT); - rctx->draw_state.shader_index = SHADER_FT_SSS_SKIN; - gl_state_enable_depth_test(); - gl_state_set_depth_mask(GL_TRUE); - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_SSS, 1); - gl_state_disable_depth_test(); - rctx->draw_state.shader_index = -1; - gl_state_bind_framebuffer(0); - uniform_value[U_NPR] = 1; - draw_pass_sss_contour(rctx, pp); - } - } - draw_pass_sss_filter(rctx, sss); - gl_state_bind_framebuffer(0); -} - -static void draw_pass_sss_contour(render_context* rctx, post_process* pp) { - pp->sss_contour_texture.bind(); - shaders_ft.state_matrix_set_mvp(mat4_identity, mat4_identity); - gl_state_enable_depth_test(); - gl_state_set_depth_func(GL_ALWAYS); - gl_state_set_depth_mask(GL_TRUE); - glViewport(0, 0, pp->render_width, pp->render_height); - gl_state_active_bind_texture_2d(0, pp->rend_texture.color_texture->tex); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - gl_state_active_bind_texture_2d(1, pp->rend_texture.depth_texture->tex); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - gl_state_active_texture(0); - shaders_ft.set(SHADER_FT_CONTOUR); - - camera* cam = rctx->camera; - float_t v3 = 1.0f / tanf((float_t)(cam->fov_rad * 0.5)); - - vec3 direction = cam->interest - cam->view_point; - float_t length = vec3::length(direction); - float_t v7 = direction.y; - if (length != 0.0f) - v7 /= length; - - float_t v9 = fabsf(v7) - 0.1f; - if (v9 < 0.0f) - v9 = 0.0f; - - shaders_ft.local_frag_set(4, v9 * 0.004f + 0.0027f, 0.003f, v3 * 0.35f, 0.0008f); - render_texture::draw_params(&shaders_ft, pp->render_width, pp->render_height, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f); -} - -static void draw_pass_sss_filter_calc_coef(double_t a1, size_t a2, double_t a3, size_t a4, - double_t* a5, double_t* a6, double_t* a7, double_t* a8) { - if (a2 > 8) - return; - - vec4 params[64] = {}; - - for (size_t i = a4; i; i--) { - float_t* v20 = (float_t*)params; - double_t v56[3]; - v56[0] = *a6; - v56[1] = *a7; - v56[2] = *a8; - - for (size_t j = 0; j < 3; j++) { - double_t v22 = 0.0; - double_t v23 = 0.0; - double_t v54[8]; - double_t v24 = 1.0 / (a3 * v56[j]); - v24 *= v24; - for (size_t k = 0; k < a2; k++) { - double_t v25 = exp(-0.5 * (v23 * v23) * v24); - v54[k] = v25; - v23 += a1; - v22 += v25; - } - - double_t v27 = 1.0 / v22; - for (size_t k = 0; k < a2; k++) - v54[k] *= v27; - - float_t* v35 = v20; - for (size_t k = 0; k < a2; k++) { - double_t v37 = v54[k] * *a5; - for (size_t v36 = 0; v36 < a2; v36++) { - *v35 += (float_t)(v37 * v54[v36]); - v35 += 4; - } - } - v20++; - } - a5++; - a6++; - a7++; - a8++; - } - shaders_ft.local_frag_set(4, a2 * a2, params); -} - -static void draw_pass_sss_filter(render_context* rctx, sss_data* a1) { - const int32_t sss_count = 6; - vec3 interest = rctx->camera->interest; - vec3 view_point = rctx->camera->view_point; - - vec3 chara_position[2]; - chara_position[0] = 0.0f; - chara_position[1] = 0.0f; - float_t chara_distance[2]; - for (int32_t i = 0; i < 2; i++) { - chara_position[i] = interest; - chara_distance[i] = 999999.0f; - rob_chara_bone_data* v16 = rob_chara_array[i].bone_data; - if (rob_chara_pv_data_array[i].type != ROB_CHARA_TYPE_NONE && rob_chara_array[i].data.field_0 & 1) { - mat4* mat = rob_chara_bone_data_get_mats_mat(v16, MOTION_BONE_N_HARA_CP); - if (mat) { - mat4_get_translation(mat, &chara_position[i]); - chara_distance[i] = vec3::distance(view_point, chara_position[i]); - } - } - } - - vec3 closest_chara_position; - if (chara_distance[0] > chara_distance[1]) { - closest_chara_position = chara_position[1]; - chara_position[0] = chara_position[1]; - } - else - closest_chara_position = chara_position[0]; - - float_t length = vec3::distance(interest, closest_chara_position); - if (length > 1.25f) - interest = chara_position[0]; - - float_t v29 = vec3::distance(view_point, interest); - if (v29 < 0.25f) - v29 = 0.25f; - float_t v31 = tanf((float_t)(rctx->camera->fov_rad * 0.5)) * 5.0f; - if (v31 < 0.25f) - v31 = 0.25f; - float_t v32 = v31 * v29; - float_t v33 = 0.6f; - float_t v34 = 1.0f / clamp_def(v32, 0.25f, 100.0f); - if (v34 < 0.145) { - float_t v36 = v34 - 0.02f; - if (v34 < 0.0f) - v36 = 0.0f; - v33 = (v36 * 8.0f) * 0.6f; - } - - shaders_ft.env_frag_set(25, v33, 0.0f, 0.0f, 0.0f); - - shaders_ft.state_matrix_set_mvp(mat4_identity, mat4_identity); - - gl_state_active_texture(0); - if (a1->npr_contour) { - a1->textures[0].bind(); - glViewport(0, 0, 640, 360); - post_process* pp = &rctx->post_process; - gl_state_bind_texture_2d(pp->rend_texture.color_texture->tex); - uniform_value[U_REDUCE] = 0; - shaders_ft.set(SHADER_FT_REDUCE); - - shaders_ft.state_matrix_set_texture(0, mat4_identity); - render_texture::draw_params(&shaders_ft, 640, 360, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f); - } - a1->textures[2].bind(); - glViewport(0, 0, 320, 180); - gl_state_bind_texture_2d(a1->textures[0].color_texture->tex); - //uniform_value[U_SSS_FILTER] = 0; - //shaders_ft.set(SHADER_FT_SSS_FILT); - shaders_ft.set(SHADER_FT_SSS_FILT_MIN); - render_texture::draw_params(&shaders_ft, 640, 360, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f); - a1->textures[1].bind(); - //uniform_value[U_SSS_FILTER] = 3; - //shaders_ft.set(SHADER_FT_SSS_FILT); - shaders_ft.set(SHADER_FT_SSS_FILT_GAUSS_2D); - shaders_ft.local_frag_set(1, 5.0f, 0.0f, 0.0f, 0.0f); - - double_t a5[3]; - double_t a6[3]; - double_t a7[3]; - double_t a8[3]; - a5[0] = 0.4; - a5[1] = 0.3; - a5[2] = 0.3; - a6[0] = 1.0; - a6[1] = 2.0; - a6[2] = 5.0; - a7[0] = 0.2; - a7[1] = 0.4; - a7[2] = 1.2; - a8[0] = 0.3; - a8[1] = 0.7; - a8[2] = 2.0; - draw_pass_sss_filter_calc_coef(1.0, sss_count, v34, 3, a5, a6, a7, a8); - - shaders_ft.local_frag_set(1, (float_t)(sss_count - 1), 0.0f, 1.0f, 1.0f); - glViewport(0, 0, 320, 180); - gl_state_bind_texture_2d(a1->textures[2].color_texture->tex); - render_texture::draw_params(&shaders_ft, 320, 180, 1.0f, 1.0f, 0.96f, 1.0f, 0.0f); - gl_state_bind_texture_2d(0); -} - -static void draw_pass_reflect(render_context* rctx, draw_pass* a1) { - render_texture& reflect_texture = rctx->draw_pass.get_render_texture(0); - reflect_texture.bind(); - if (draw_task_get_count(rctx, DRAW_OBJECT_REFLECT_OPAQUE) - || draw_task_get_count(rctx, DRAW_OBJECT_REFLECT_TRANSPARENT) - || draw_task_get_count(rctx, DRAW_OBJECT_REFLECT_TRANSLUCENT) - || draw_task_get_count(rctx, DRAW_OBJECT_REFLECT_CHARA_OPAQUE) - || draw_task_get_count(rctx, DRAW_OBJECT_REFLECT_CHARA_TRANSPARENT) - || draw_task_get_count(rctx, DRAW_OBJECT_REFLECT_CHARA_TRANSLUCENT)) { - texture* refl_tex = reflect_texture.color_texture; - glViewport(0, 0, refl_tex->width, refl_tex->height); - - draw_pass_set_camera(rctx->camera); - - for (int32_t i = LIGHT_SET_MAIN; i < LIGHT_SET_MAX; i++) - rctx->light_set[i].data_set(rctx->face, (light_set_id)i); - for (int32_t i = FOG_DEPTH; i < FOG_BUMP; i++) - rctx->fog[i].data_set((fog_id)i); - - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - rctx->draw_state.shader_index = SHADER_FT_S_REFL; - - light_set* set = &rctx->light_set[LIGHT_SET_MAIN]; - light_clip_plane clip_plane; - set->lights[LIGHT_SPOT].get_clip_plane(clip_plane); - uniform_value[U_REFLECT] = 2; - uniform_value[U_CLIP_PLANE] = clip_plane.data[1]; - - gl_state_enable_depth_test(); - gl_state_set_depth_func(GL_LEQUAL); - gl_state_set_depth_mask(GL_TRUE); - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_REFLECT_OPAQUE, 0, 0, a1->field_31D); - if (a1->reflect_type == STAGE_DATA_REFLECT_REFLECT_MAP) { - gl_state_enable_blend(); - gl_state_set_blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - gl_state_set_blend_equation(GL_FUNC_ADD); - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_REFLECT_TRANSLUCENT); - gl_state_disable_blend(); - } - - if (a1->reflect_type == STAGE_DATA_REFLECT_REFLECT_MAP) { - uniform_value[U_REFLECT] = a1->field_31E; - uniform_value[U_CLIP_PLANE] = clip_plane.data[0]; - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_REFLECT_CHARA_OPAQUE); - } - gl_state_disable_depth_test(); - uniform_value[U_REFLECT] = 0; - rctx->draw_state.shader_index = -1; - - reflect_texture.bind(); - for (int32_t i = a1->reflect_blur_num; i > 0; i--) - blur_filter_apply(reflect_texture.color_texture->tex, - reflect_texture.color_texture->tex, a1->reflect_blur_filter); - } - else { - vec4 clear_color; - glGetFloatv(GL_COLOR_CLEAR_VALUE, (GLfloat*)&clear_color); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT); - glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); - } -} - -static void draw_pass_refract(render_context* rctx, draw_pass* a1) { - render_texture& refract_texture = rctx->draw_pass.get_render_texture(1); - refract_texture.bind(); - if (draw_task_get_count(rctx, DRAW_OBJECT_REFRACT_OPAQUE) - || draw_task_get_count(rctx, DRAW_OBJECT_REFRACT_TRANSPARENT) - || draw_task_get_count(rctx, DRAW_OBJECT_REFRACT_TRANSLUCENT)) { - texture* refr_tex = refract_texture.color_texture; - glViewport(0, 0, refr_tex->width, refr_tex->height); - - draw_pass_set_camera(rctx->camera); - - for (int32_t i = LIGHT_SET_MAIN; i < LIGHT_SET_MAX; i++) - rctx->light_set[i].data_set(rctx->face, (light_set_id)i); - for (int32_t i = FOG_DEPTH; i < FOG_BUMP; i++) - rctx->fog[i].data_set((fog_id)i); - - vec4 clear_color; - glGetFloatv(GL_COLOR_CLEAR_VALUE, (GLfloat*)&clear_color); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); - - rctx->draw_state.shader_index = SHADER_FT_S_REFR; - gl_state_enable_depth_test(); - gl_state_set_depth_func(GL_LEQUAL); - gl_state_set_depth_mask(GL_TRUE); - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_REFRACT_OPAQUE); - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_REFRACT_TRANSPARENT); - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_REFRACT_TRANSLUCENT); - gl_state_disable_depth_test(); - rctx->draw_state.shader_index = -1; - } - else { - vec4 clear_color; - glGetFloatv(GL_COLOR_CLEAR_VALUE, (GLfloat*)&clear_color); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT); - glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); - } -} - -static void draw_pass_preprocess(render_context* rctx, draw_pass* a1) { - for (draw_preprocess& i : a1->preprocess) - if (i.func) - i.func(i.data); -} - -void sub_1404A9350(post_process* a1, bool a2) { - if (a2) { - a1->aet_back_texture.bind(); - a1->aet_back = 1; - } - else - a1->rend_texture.bind(); - glViewport(0, 0, a1->render_width, a1->render_height); -} - -static void draw_pass_clear(render_context* rctx, draw_pass* a1) { - if (true/*a1->field_128*/) { - vec4 clear_color = get_clear_color(); - glClearBufferfv(GL_COLOR, 0, (GLfloat*)&clear_color); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - } - - post_process* pp = &rctx->post_process; - if (!a1->sss_data.enable || !a1->sss_data.npr_contour) { - pp->set_render_texture(); - - if (false/*sub_14063FA90(2)*/) { - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClearDepth(1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - } - else { - vec4 clear_color = get_clear_color(); - glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); - glClearDepth(1.0f); - if (true/*a1->field_128*/) - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - else - glClear(GL_DEPTH_BUFFER_BIT); - } - gl_state_bind_framebuffer(0); - } - else { - if (false/*sub_14063FA90(2)*/) { - pp->set_render_texture(); - - vec4 clear_color = 0.0f; - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT); - gl_state_bind_framebuffer(0); - } - else if (false/*a1->field_128*/) { - pp->set_render_texture(); - - vec4 clear_color = get_clear_color(); - glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); - glClear(GL_COLOR_BUFFER_BIT); - gl_state_bind_framebuffer(0); - } - } - glGetError(); -} - -static void draw_pass_pre_sprite(render_context* rctx, draw_pass* a1) { - if (!(false/*sub_14063FA90(2)*/)) - return; - - post_process* pp = &rctx->post_process; - pp->set_render_texture(true); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT); - shaders_ft.state_matrix_set_mvp(mat4_identity, rctx->camera->projection_aet_3d); - gl_state_set_depth_mask(GL_FALSE); - gl_state_disable_depth_test(); - gl_state_enable_blend(); - gl_state_disable_cull_face(); - //sub_14063F870(2, 1, pp->aa->mlaa_buffer[1].color_texture->tex); - gl_state_enable_cull_face(); - gl_state_disable_blend(); - gl_state_enable_depth_test(); - gl_state_set_depth_mask(GL_TRUE); - gl_state_bind_framebuffer(0); - glGetError(); -} - -static void draw_pass_3d(render_context* rctx, draw_pass* a1) { - rctx->camera->update_data(); - rctx->view_mat = rctx->camera->view; - rctx->post_process.set_render_texture(); - draw_pass_set_camera(rctx->camera); - if (!a1->sss_data.enable || !a1->sss_data.npr_contour || draw_pass_3d_get_translucent_count(rctx)) - glClear(GL_DEPTH_BUFFER_BIT); - - gl_state_set_depth_func(GL_LEQUAL); - - for (int32_t i = LIGHT_SET_MAIN; i < LIGHT_SET_MAX; i++) - rctx->light_set[i].data_set(rctx->face, (light_set_id)i); - for (int32_t i = FOG_DEPTH; i < FOG_MAX; i++) - rctx->fog[i].data_set((fog_id)i); - - if (a1->shadow) - draw_pass_3d_shadow_set(a1->shadow_ptr, rctx); - else - draw_pass_3d_shadow_reset(rctx); - - if (a1->enable[DRAW_PASS_REFLECT] && a1->reflect) { - render_texture& reflect_texture = a1->get_render_texture(0); - gl_state_active_bind_texture_2d(15, reflect_texture.color_texture->tex); - uniform_value[U_WATER_REFLECT] = 1; - } - else - uniform_value[U_WATER_REFLECT] = 0; - - uniform_value[U12] = a1->field_320 ? 1 : 0; - - if (a1->effect_texture) - gl_state_active_bind_texture_2d(14, a1->effect_texture->tex); - - gl_state_active_bind_texture_2d(16, a1->sss_data.textures[1].color_texture->tex); - gl_state_active_texture(0); - - if (a1->alpha_z_sort) { - draw_task_sort(rctx, DRAW_OBJECT_TRANSLUCENT, 1); - draw_task_sort(rctx, DRAW_OBJECT_TRANSLUCENT_NO_SHADOW, 2); - draw_task_sort(rctx, DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_1, 1); - draw_task_sort(rctx, DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_2, 1); - draw_task_sort(rctx, DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_3, 1); - } - - if (a1->opaque_z_sort) - draw_task_sort(rctx, DRAW_OBJECT_OPAQUE, 0); - - if (a1->draw_pass_3d[DRAW_PASS_3D_OPAQUE]) { - gl_state_enable_depth_test(); - gl_state_set_depth_mask(GL_TRUE); - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_OPAQUE); - gl_state_disable_depth_test(); - } - - Glitter::glt_particle_manager->DispScenes(Glitter::DISP_OPAQUE); - - if (draw_grid_3d) - draw_pass_3d_grid(rctx); - - gl_state_enable_depth_test(); - gl_state_set_depth_mask(GL_TRUE); - if (a1->draw_pass_3d[DRAW_PASS_3D_TRANSPARENT]) - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_TRANSPARENT); - /*if (rctx->draw_pass.field_120) - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_TYPE_7);*/ - - rctx->post_process.exposure->get_exposure_chara_data(&rctx->post_process, rctx->camera); - - if (a1->npr_param == 1) - draw_pass_3d_contour(rctx, a1); - - rctx->post_process.draw_lens_flare(rctx->camera); - //star_catalog_data.draw(); - - draw_task_draw_objects_by_type_translucent(rctx, - a1->draw_pass_3d[DRAW_PASS_3D_OPAQUE], - a1->draw_pass_3d[DRAW_PASS_3D_TRANSPARENT], - a1->draw_pass_3d[DRAW_PASS_3D_TRANSLUCENT], - DRAW_OBJECT_OPAQUE_ALPHA_ORDER_3, - DRAW_OBJECT_TRANSPARENT_ALPHA_ORDER_3, - DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_3); - - snow_particle_draw(); - rain_particle_draw(); - leaf_particle_draw(); - particle_draw(); - gl_state_disable_depth_test(); - - gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); - Glitter::glt_particle_manager->DispScenes(Glitter::DISP_ALPHA); - gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - - /*if (a1->npr_param == 1) { - gl_state_set_color_mask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT); - gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - }*/ - - gl_state_enable_depth_test(); - - if (a1->draw_pass_3d[DRAW_PASS_3D_TRANSLUCENT]) { - gl_state_enable_blend(); - gl_state_set_blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - gl_state_set_depth_mask(GL_FALSE); - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_TRANSLUCENT_NO_SHADOW); - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_TRANSLUCENT); - gl_state_disable_blend(); - } - - gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); // X - Glitter::glt_particle_manager->DispScenes(Glitter::DISP_TYPE_2); - gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - - gl_state_set_depth_mask(GL_TRUE); - draw_task_draw_objects_by_type_translucent(rctx, - a1->draw_pass_3d[DRAW_PASS_3D_OPAQUE], - a1->draw_pass_3d[DRAW_PASS_3D_TRANSPARENT], - a1->draw_pass_3d[DRAW_PASS_3D_TRANSLUCENT], - DRAW_OBJECT_OPAQUE_ALPHA_ORDER_2, - DRAW_OBJECT_TRANSPARENT_ALPHA_ORDER_2, - DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_2); - gl_state_disable_depth_test(); - - gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); - Glitter::glt_particle_manager->DispScenes(Glitter::DISP_NORMAL); - gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - - gl_state_enable_depth_test(); - gl_state_set_depth_mask(GL_TRUE); - draw_task_draw_objects_by_type_translucent(rctx, - a1->draw_pass_3d[DRAW_PASS_3D_OPAQUE], - a1->draw_pass_3d[DRAW_PASS_3D_TRANSPARENT], - a1->draw_pass_3d[DRAW_PASS_3D_TRANSLUCENT], - DRAW_OBJECT_OPAQUE_ALPHA_ORDER_1, - DRAW_OBJECT_TRANSPARENT_ALPHA_ORDER_1, - DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_1); - - if (Glitter::glt_particle_manager->CheckHasLocalEffect()) { // X - camera* cam = rctx->camera; - double_t fov = cam->get_fov(); - cam->set_fov(32.2673416137695); - draw_pass_set_camera(cam); - - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_OPAQUE_LOCAL); - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_TRANSPARENT_LOCAL); - gl_state_enable_blend(); - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_TRANSLUCENT_LOCAL); - gl_state_disable_blend(); - - draw_task_draw_objects_by_type_translucent(rctx, - a1->draw_pass_3d[DRAW_PASS_3D_OPAQUE], - a1->draw_pass_3d[DRAW_PASS_3D_TRANSPARENT], - a1->draw_pass_3d[DRAW_PASS_3D_TRANSLUCENT], - DRAW_OBJECT_OPAQUE_ALPHA_ORDER_2_LOCAL, - DRAW_OBJECT_TRANSPARENT_ALPHA_ORDER_2_LOCAL, - DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_2_LOCAL); - - gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); - Glitter::glt_particle_manager->DispScenes(Glitter::DISP_LOCAL); - gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - cam->set_fov(fov); - draw_pass_set_camera(cam); - } - - if (a1->effect_texture) - gl_state_active_bind_texture_2d(14, 0); - - gl_state_disable_depth_test(); - - if (a1->enable[DRAW_PASS_REFLECT] && a1->reflect) - gl_state_active_bind_texture_2d(15, 0); - /*if (a1->shadow) - draw_pass_3d_shadow_reset();*/ - shader::unbind(); -} - -static void draw_pass_3d_contour(render_context* rctx, draw_pass* a1) { - render_texture* rt = &rctx->post_process.rend_texture; - render_texture* contour_rt = &rctx->post_process.sss_contour_texture; - - gl_state_enable_blend(); - gl_state_set_blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - gl_state_set_blend_equation(GL_FUNC_ADD); - gl_state_disable_depth_test(); - - shaders_ft.set(SHADER_FT_CONTOUR_NPR); - shaders_ft.state_matrix_set_mvp(mat4_identity, mat4_identity); - gl_state_active_bind_texture_2d(16, contour_rt->color_texture->tex); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - gl_state_active_bind_texture_2d(17, contour_rt->depth_texture->tex); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - gl_state_active_bind_texture_2d(14, rt->depth_texture->tex); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - render_texture::draw_custom(&shaders_ft); - - draw_pass_set_camera(rctx->camera); - - if (a1->effect_texture) - gl_state_active_bind_texture_2d(14, a1->effect_texture->tex); - gl_state_enable_depth_test(); - gl_state_disable_blend(); -} - -static int32_t draw_pass_3d_get_translucent_count(render_context* rctx) { - int32_t count = 0; - count += draw_task_get_count(rctx, DRAW_OBJECT_OPAQUE_ALPHA_ORDER_1); - count += draw_task_get_count(rctx, DRAW_OBJECT_TRANSPARENT_ALPHA_ORDER_1); - count += draw_task_get_count(rctx, DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_1); - count += draw_task_get_count(rctx, DRAW_OBJECT_OPAQUE_ALPHA_ORDER_2); - count += draw_task_get_count(rctx, DRAW_OBJECT_TRANSPARENT_ALPHA_ORDER_2); - count += draw_task_get_count(rctx, DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_2); - count += draw_task_get_count(rctx, DRAW_OBJECT_OPAQUE_ALPHA_ORDER_3); - count += draw_task_get_count(rctx, DRAW_OBJECT_TRANSPARENT_ALPHA_ORDER_3); - count += draw_task_get_count(rctx, DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_3); - return count; -} - -static void draw_pass_3d_shadow_reset(render_context* rctx) { - gl_state_active_bind_texture_2d(6, 0); - gl_state_active_bind_texture_2d(7, 0); - shaders_ft.state_matrix_set_texture(6, mat4_identity); - shaders_ft.state_matrix_set_texture(7, mat4_identity); - rctx->draw_state.self_shadow = false; - rctx->draw_state.light = false; -} - -static void draw_pass_3d_shadow_set(shadow* shad, render_context* rctx) { - if (shad->self_shadow && shad->field_2EC > 0) { - gl_state_active_bind_texture_2d(19, shad->field_8[3].color_texture->tex); - gl_state_active_bind_texture_2d(20, shad->field_8[5].color_texture->tex); - gl_state_active_texture(0); - shaders_ft.env_frag_set(23, - ((shad->field_2D8 * shad->field_208) * 2.0f) * 1.442695f, 0.0f, 0.0f, 0.0f); - rctx->draw_state.self_shadow = true; - } - else - rctx->draw_state.self_shadow = false; - - if (shad->field_2EC > 0) { - rctx->draw_state.light = true; - uniform_value[U_LIGHT_1] = shad->field_2EC > 1 ? 1 : 0; - - float_t v7 = shad->view_region * shad->range; - for (int32_t i = 0; i < 2; i++) { - float_t v6 = 0.0f; - vec3* interest; - vec3* view_point; - if (shad->field_2F5) { - v6 = i ? 0.5f : -0.5f; - - interest = &shad->interest[i]; - view_point = &shad->view_point[i]; - } - else { - interest = &shad->interest_shared; - view_point = &shad->view_point_shared; - } - - mat4 temp; - mat4_translate(0.5f, 0.5f, 0.5f, &temp); - mat4_scale_rot(&temp, 0.5f, 0.5f, 0.5f, &temp); - mat4_translate_mult(&temp, v6, 0.0f, 0.0f, &temp); - - mat4 proj; - mat4_ortho(-v7, v7, -v7, v7, shad->z_near, shad->z_far, &proj); - mat4_mult(&proj, &temp, &proj); - - mat4 view; - mat4_look_at(view_point, interest, &view); - mat4_mult(&view, &proj, &view); - shaders_ft.state_matrix_set_texture(6ULL + i, view); - } - - for (int32_t i = 0, j = 0; i < 2; i++) { - if (!shad->field_2F0[i]) - continue; - - gl_state_active_bind_texture_2d(6 + j, shad->field_158[1 + i]->color_texture->tex); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f); - j++; - } - gl_state_active_texture(0); - - light_set* set = &rctx->light_set[LIGHT_SET_MAIN]; - light_data* data = &set->lights[LIGHT_SHADOW]; - - vec4 ambient; - if (data->get_type() == LIGHT_PARALLEL) - data->get_ambient(ambient); - else { - ambient.x = shad->ambient; - ambient.y = shad->ambient; - ambient.z = shad->ambient; - } - - shaders_ft.env_frag_set(12, ambient.x, ambient.y, ambient.z, 1.0f); - shaders_ft.env_frag_set(13, 1.0f - ambient.x, 1.0f - ambient.y, 1.0f - ambient.z, 0.0f); - } - else { - rctx->draw_state.light = false; - - for (int32_t i = 0; i < 2; i++) - gl_state_active_bind_texture_2d(6 + i, shad->field_158[1 + i]->color_texture->tex); - gl_state_active_texture(0); - - shaders_ft.env_frag_set(12, 1.0f); - shaders_ft.env_frag_set(13, 0.0f); - } -} - -static void draw_pass_show_vector(render_context* rctx, draw_pass* a1) { - if (!a1->show_vector_flags) - return; - - rctx->camera->update_data(); - rctx->view_mat = rctx->camera->view; - rctx->post_process.set_render_texture(); - draw_pass_set_camera(rctx->camera); - - shaders_ft.env_vert_set(20, a1->show_vector_length, a1->show_vector_z_offset, 0.0f, 0.0f); - for (int32_t i = 1; i < 4; i++) { - if (!(a1->show_vector_flags & (1 << (i - 1)))) - continue; - - if (a1->draw_pass_3d[DRAW_PASS_3D_OPAQUE]) - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_OPAQUE, 0, i, true); - if (a1->draw_pass_3d[DRAW_PASS_3D_TRANSPARENT]) - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_TRANSPARENT, 0, i, true); - if (a1->draw_pass_3d[DRAW_PASS_3D_TRANSLUCENT]) { - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_TRANSLUCENT_NO_SHADOW, 0, i, true); - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_TRANSLUCENT, 0, i, true); - } - } -} - -static void draw_pass_post_process(render_context* rctx, draw_pass* a1) { - rctx->camera->update_data(); - - texture* light_proj_tex = 0; - light_proj* litproj = rctx->litproj; - if (litproj && litproj->enable) { - light_set* set = &rctx->light_set[LIGHT_SET_MAIN]; - if (set->lights[LIGHT_PROJECTION].get_type() == LIGHT_SPOT - && texture_storage_get_texture(litproj->texture_id)) - light_proj_tex = litproj->draw_texture.color_texture; - } - - rctx->post_process.apply(rctx->camera, light_proj_tex, a1->npr_param); -} - -static void draw_pass_sprite(render_context* rctx, draw_pass* a1) { - post_process* pp = &rctx->post_process; - - if (a1->multisample && a1->multisample_framebuffer) { - glBindFramebuffer(GL_FRAMEBUFFER, a1->multisample_framebuffer); - gl_state_enable_multisample(); - glClearColor(0.0, 0.0, 0.0, 0.0); - glClear(GL_COLOR_BUFFER_BIT); - } - - shaders_ft.state_matrix_set_mvp(mat4_identity, rctx->camera->projection_aet_2d); - - gl_state_disable_depth_test(); - gl_state_enable_blend(); - gl_state_disable_cull_face(); - //sub_14063F870(0, 1, pp->aa->mlaa_buffer[1].color_texture->tex); - gl_state_enable_cull_face(); - gl_state_disable_blend(); - gl_state_enable_depth_test(); - - if (a1->multisample && a1->multisample_framebuffer) { - glBindFramebuffer(GL_FRAMEBUFFER, 0); - gl_state_disable_multisample(); - gl_state_bind_read_framebuffer(a1->multisample_framebuffer); - glBlitFramebuffer(0, 0, pp->sprite_width, pp->sprite_height, - 0, 0, pp->sprite_width, pp->sprite_height, GL_COLOR_BUFFER_BIT, GL_NEAREST); - gl_state_bind_read_framebuffer(0); - } - glGetError(); -} - -inline static void draw_pass_end(draw_pass* pass, draw_pass_type type) { - pass->cpu_time[type] = pass->time.calc_time(); - if (pass->wait_for_gpu) { - time_struct t; - glFinish(); - pass->gpu_time[type] = t.calc_time(); - } - else - pass->gpu_time[type] = 0; -} - -static void draw_pass_3d_grid(render_context* rctx) { - rctx->camera->update_data(); - - gl_state_enable_blend(); - gl_state_set_blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - gl_state_set_blend_equation(GL_FUNC_ADD); - gl_state_enable_depth_test(); - gl_state_set_depth_mask(GL_TRUE); - - grid_shader->use(); - grid_shader->set("vp", false, rctx->camera->view_projection); - gl_state_bind_vertex_array(grid_vao); - glDrawArrays(GL_LINES, 0, (GLsizei)grid_vertex_count); - gl_state_use_program(0); - - gl_state_disable_depth_test(); - gl_state_set_depth_mask(GL_FALSE); - gl_state_disable_blend(); -} - -static void draw_pass_set_camera(camera* cam) { - cam->update_data(); - shaders_ft.state_matrix_set_mvp(cam->view, cam->projection); - shaders_ft.state_matrix_set_program(5, cam->view); - - double_t fov_horizontal_rad = tan(cam->fov_rad * 0.5); - - shaders_ft.env_frag_set(20, - (float_t)(0.5 / (fov_horizontal_rad * cam->aspect)), - (float_t)(0.5 / fov_horizontal_rad), - 0.0f, 0.0f); - shaders_ft.env_frag_set(32, - (float_t)(cam->max_distance / (cam->max_distance - cam->min_distance)), - (float_t)(-(cam->max_distance * cam->min_distance) / (cam->max_distance - cam->min_distance)), - 0.0f, 0.0f); -} - -static void texture_params_get(GLuint tex_0, texture_param* tex_0_param, - GLuint tex_1, texture_param* tex_1_param, GLuint tex_2, texture_param* tex_2_param) { - gl_state_disable_depth_test(); - if (tex_0_param) { - tex_0_param->width = 0; - tex_0_param->height = 0; - if (tex_0) { - gl_state_bind_texture_2d(tex_0); - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &tex_0_param->width); - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &tex_0_param->height); - } - } - - if (tex_1_param) { - tex_1_param->width = 0; - tex_1_param->height = 0; - if (tex_1) { - gl_state_bind_texture_2d(tex_1); - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &tex_1_param->width); - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &tex_1_param->height); - } - } - - if (tex_2_param) { - tex_2_param->width = 0; - tex_2_param->height = 0; - if (tex_2) { - gl_state_bind_texture_2d(tex_2); - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &tex_2_param->width); - glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &tex_2_param->height); - } - } - gl_state_bind_texture_2d(0); - - if (tex_0_param) { - mat4 mat; - mat4_ortho(0.0f, (float_t)tex_0_param->width, - 0.0f, (float_t)tex_0_param->height, -1.0f, 1.0f, &mat); - shaders_ft.state_matrix_set_mvp(mat4_identity, mat); - glViewport(0, 0, tex_0_param->width, tex_0_param->height); - } -} - -static void texture_params_restore(texture_param* tex_0_param, - texture_param* tex_1_param, texture_param* tex_2_param) { - for (int32_t i = 0; i < 4; i++) - gl_state_active_bind_texture_2d(i, 0); -} - -static void blur_filter_apply(GLuint tex_0, GLuint tex_1, blur_filter_mode filter) { - if (!tex_0 || !tex_1) - return; - - texture_param tex_params[2]; - texture_params_get(tex_0, &tex_params[0], tex_1, &tex_params[1], 0, 0); - uniform_value[U_IMAGE_FILTER] = filter == BLUR_FILTER_32 ? 1 : 0; - shaders_ft.set(SHADER_FT_IMGFILT); - - float_t scale = filter == BLUR_FILTER_32 ? (float_t)(1.0 / 8.0) : (float_t)(1.0 / 4.0); - shaders_ft.local_frag_set(0, scale, scale, scale, scale); - shaders_ft.local_frag_set(1, 0.0f, 0.0f, 0.0f, 0.0f); - float_t w = 1.0f / (float_t)tex_params[1].width; - float_t h = 1.0f / (float_t)tex_params[1].height; - switch (filter) { - case BLUR_FILTER_4: { - mat4 m[4]; - mat4_translate( 1.0f * w, 0.0f * h, 0.0f, &m[0]); - mat4_translate( 0.0f * w, 1.0f * h, 0.0f, &m[1]); - mat4_translate( 1.0f * w, -1.0f * h, 0.0f, &m[2]); - mat4_translate( 0.0f * w, -1.0f * h, 0.0f, &m[3]); - shaders_ft.state_matrix_set_texture(0, m[0]); - shaders_ft.state_matrix_set_texture(1, m[1]); - shaders_ft.state_matrix_set_texture(2, m[2]); - shaders_ft.state_matrix_set_texture(3, m[3]); - } break; - case BLUR_FILTER_9: { - mat4 m[4]; - mat4_translate(-0.5f * w, 0.5f * h, 0.0f, &m[0]); - mat4_translate( 0.5f * w, 0.5f * h, 0.0f, &m[1]); - mat4_translate(-0.5f * w, -0.5f * h, 0.0f, &m[2]); - mat4_translate( 0.5f * w, -0.5f * h, 0.0f, &m[3]); - shaders_ft.state_matrix_set_texture(0, m[0]); - shaders_ft.state_matrix_set_texture(1, m[1]); - shaders_ft.state_matrix_set_texture(2, m[2]); - shaders_ft.state_matrix_set_texture(3, m[3]); - } break; - case BLUR_FILTER_16: { - mat4 m[4]; - mat4_translate( 0.5f * w, -1.5f * h, 0.0f, &m[0]); - mat4_translate(-1.5f * w, 0.5f * h, 0.0f, &m[1]); - mat4_translate( 1.5f * w, -0.5f * h, 0.0f, &m[2]); - mat4_translate(-0.5f * w, 1.5f * h, 0.0f, &m[3]); - shaders_ft.state_matrix_set_texture(0, m[0]); - shaders_ft.state_matrix_set_texture(1, m[1]); - shaders_ft.state_matrix_set_texture(2, m[2]); - shaders_ft.state_matrix_set_texture(3, m[3]); - } break; - case BLUR_FILTER_32: { - mat4 m[8]; - mat4_translate( 3.5f * w, -3.5f * h, 0.0f, &m[0]); - mat4_translate(-1.5f * w, -3.5f * h, 0.0f, &m[1]); - mat4_translate( 1.5f * w, -1.5f * h, 0.0f, &m[2]); - mat4_translate(-3.5f * w, -1.5f * h, 0.0f, &m[3]); - mat4_translate( 3.5f * w, 1.5f * h, 0.0f, &m[4]); - mat4_translate( 1.5f * w, 1.5f * h, 0.0f, &m[5]); - mat4_translate( 1.5f * w, 3.5f * h, 0.0f, &m[6]); - mat4_translate(-3.5f * w, 3.5f * h, 0.0f, &m[7]); - shaders_ft.state_matrix_set_texture(0, m[0]); - shaders_ft.state_matrix_set_texture(1, m[1]); - shaders_ft.state_matrix_set_texture(2, m[2]); - shaders_ft.state_matrix_set_texture(3, m[3]); - shaders_ft.state_matrix_set_texture(4, m[4]); - shaders_ft.state_matrix_set_texture(5, m[5]); - shaders_ft.state_matrix_set_texture(6, m[6]); - shaders_ft.state_matrix_set_texture(7, m[7]); - } break; - } - gl_state_active_bind_texture_2d(0, tex_1); - shaders_ft.state_matrix_set_mvp(mat4_identity); - render_texture::draw_custom(&shaders_ft); - texture_params_restore(&tex_params[0], &tex_params[1], 0); -} diff --git a/src/CRE/draw_pass.hpp b/src/CRE/draw_pass.hpp deleted file mode 100644 index f8c1ee07..00000000 --- a/src/CRE/draw_pass.hpp +++ /dev/null @@ -1,13 +0,0 @@ -/* - by korenkonder - GitHub/GitLab: korenkonder -*/ - -#pragma once - -#include "../KKdLib/default.hpp" -#include "../KKdLib/mat.hpp" -#include "../KKdLib/vec.hpp" -#include "draw_task.hpp" - -extern void draw_pass_main(render_context* rctx); diff --git a/src/CRE/draw_task.cpp b/src/CRE/draw_task.cpp deleted file mode 100644 index c33e7e45..00000000 --- a/src/CRE/draw_task.cpp +++ /dev/null @@ -1,1021 +0,0 @@ -/* - by korenkonder - GitHub/GitLab: korenkonder -*/ - -#include "draw_task.hpp" -#include "../KKdLib/hash.hpp" -#include "../KKdLib/sort.hpp" -#include "camera.hpp" -#include "fbo.hpp" -#include "post_process.hpp" -#include "shader_ft.hpp" - -static void draw_task_add(render_context* rctx, draw_object_type type, draw_task* task); -static void draw_task_preprocess_init(draw_task* task, const mat4* mat, - void(*func)(render_context* rctx, void* data), void* data); -static void draw_task_object_init(draw_task* task, object_data* object_data, const mat4* mat, - float_t bounding_radius, obj_sub_mesh* sub_mesh, obj_mesh* mesh, obj_material_data* material, - std::vector* textures, int32_t mat_count, mat4* mats, GLuint array_buffer, - GLuint element_array_buffer, vec4* blend_color, vec4* emission, int32_t morph_array_buffer, - int32_t instances_count, mat4* instances_mat, void(*draw_object_func)(draw_object*)); -static void draw_task_object_translucent_init(draw_task* task, const mat4* mat, - draw_task_object_translucent* object); -static int draw_task_sort_quicksort_compare0(void const* src1, void const* src2); -static int draw_task_sort_quicksort_compare1(void const* src1, void const* src2); -static int draw_task_sort_quicksort_compare2(void const* src1, void const* src2); -static int32_t draw_task_translucent_sort_count_layers(render_context* rctx, - int32_t* alpha_array, draw_object_type opaque, - draw_object_type transparent, draw_object_type translucent); -static void draw_task_translucent_sort_has_objects(render_context* rctx, bool* arr, draw_object_type type); - -void draw_task_draw_objects_by_type(render_context* rctx, draw_object_type type, - int32_t a2, int32_t show_vector, bool a4, int32_t alpha) { - if (draw_task_get_count(rctx, type) < 1) - return; - - int32_t alpha_test = 0; - float_t min_alpha = 1.0f; - bool reflect = uniform_value[U_REFLECT] == 1; - void(*draw_object_func)(render_context* rctx, draw_object* draw) = draw_object_draw_default; - - for (int32_t i = 0; i < 6; i++) - gl_state_active_bind_texture_2d(i, 0); - gl_state_active_texture(0); - gl_state_set_blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - gl_state_set_primitive_restart_index(0xFFFF); - uniform_value_reset(); - gl_state_get(); - - switch (type) { - case DRAW_OBJECT_TRANSLUCENT: - case DRAW_OBJECT_TRANSLUCENT_NO_SHADOW: - case DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_1: - case DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_2: - case DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_3: - case DRAW_OBJECT_TRANSLUCENT_LOCAL: - case DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_2_LOCAL: - if (a2) - draw_object_func = draw_object_draw_translucent; - else - gl_state_set_depth_mask(GL_FALSE); - min_alpha = 0.0; - break; - case DRAW_OBJECT_TRANSPARENT: - case DRAW_OBJECT_TRANSPARENT_ALPHA_ORDER_1: - case DRAW_OBJECT_TRANSPARENT_ALPHA_ORDER_2: - case DRAW_OBJECT_TRANSPARENT_ALPHA_ORDER_3: - case DRAW_OBJECT_TRANSPARENT_LOCAL: - case DRAW_OBJECT_TRANSPARENT_ALPHA_ORDER_2_LOCAL: - alpha_test = 1; - min_alpha = 0.1f; - break; - case DRAW_OBJECT_SHADOW_CHARA: - case DRAW_OBJECT_SHADOW_STAGE: - case DRAW_OBJECT_SHADOW_OBJECT_CHARA: - case DRAW_OBJECT_SHADOW_OBJECT_STAGE: - draw_object_func = draw_object_draw_shadow; - min_alpha = 0.5f; - break; - case DRAW_OBJECT_TYPE_6: - draw_object_func = draw_object_draw_translucent; - gl_state_set_color_mask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - rctx->draw_state.shader_index = SHADER_FT_SIL; - break; - case DRAW_OBJECT_TYPE_7: - draw_object_func = draw_object_draw_translucent; - gl_state_set_color_mask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); - rctx->draw_state.shader_index = SHADER_FT_SIL; - alpha_test = 1; - min_alpha = 0.0f; - break; - case DRAW_OBJECT_REFLECT_CHARA_OPAQUE: - gl_state_set_cull_face_mode(GL_FRONT); - if (reflect) - draw_object_func = draw_object_draw_reflect; - else if (rctx->draw_pass.reflect_type == STAGE_DATA_REFLECT_REFLECT_MAP) - draw_object_func = draw_object_draw_reflect_reflect_map; - break; - case DRAW_OBJECT_REFLECT_CHARA_TRANSLUCENT: - gl_state_set_cull_face_mode(GL_FRONT); - if (reflect) - draw_object_func = draw_object_draw_reflect; - else if (rctx->draw_pass.reflect_type == STAGE_DATA_REFLECT_REFLECT_MAP) - draw_object_func = draw_object_draw_reflect_reflect_map; - min_alpha = 0.0f; - break; - case DRAW_OBJECT_REFLECT_CHARA_TRANSPARENT: - gl_state_set_cull_face_mode(GL_FRONT); - alpha_test = 1; - min_alpha = 0.1f; - if (reflect) - draw_object_func = draw_object_draw_reflect; - else { - if (rctx->draw_pass.reflect_type == STAGE_DATA_REFLECT_REFLECT_MAP) - draw_object_func = draw_object_draw_reflect_reflect_map; - } - break; - case DRAW_OBJECT_REFLECT_OPAQUE: - alpha_test = 1; - if (!a4) - draw_object_func = draw_object_draw_reflect_reflect_map; - break; - case DRAW_OBJECT_REFLECT_TRANSLUCENT: - case DRAW_OBJECT_REFRACT_TRANSLUCENT: - gl_state_set_depth_mask(GL_FALSE); - min_alpha = 0.0f; - break; - case DRAW_OBJECT_REFLECT_TRANSPARENT: - case DRAW_OBJECT_REFRACT_TRANSPARENT: - alpha_test = 1; - min_alpha = 0.1f; - break; - case DRAW_OBJECT_SSS: - draw_object_func = draw_object_draw_sss; - break; - case DRAW_OBJECT_PREPROCESS: - draw_object_func = draw_object_draw_translucent; - break; - default: - break; - } - shaders_ft.env_frag_set(21, 0.0f, 0.0f, 0.0f, min_alpha); - uniform_value[U_ALPHA_TEST] = alpha_test; - - std::vector& vec = rctx->object_data.draw_task_array[type]; - if (alpha < 0) - for (draw_task*& i : vec) { - draw_task* task = i; - switch (task->type) { - case DRAW_TASK_OBJECT: { - draw_object_draw(rctx, &task->data.object, - &task->mat, draw_object_func, show_vector); - } break; - case DRAW_TASK_OBJECT_PRIMITIVE: { - draw_object_model_mat_load(rctx, &task->mat); - //draw_primitive_draw(&task->data.primitive); - } break; - case DRAW_TASK_OBJECT_PREPROCESS: { - draw_object_model_mat_load(rctx, &task->mat); - task->data.preprocess.func(rctx, task->data.preprocess.data); - } break; - case DRAW_TASK_OBJECT_TRANSLUCENT: { - for (uint32_t j = 0; j < task->data.object_translucent.count; j++) - draw_object_draw(rctx, task->data.object_translucent.objects[j], - &task->mat, draw_object_func, show_vector); - } break; - } - } - else - for (draw_task*& i : vec) { - draw_task* task = i; - switch (task->type) { - case DRAW_TASK_OBJECT: { - int32_t a = (int32_t)(task->data.object.blend_color.w * 255.0f); - a = clamp_def(a, 0, 255); - if (a == alpha) { - draw_object_draw(rctx, &task->data.object, - &task->mat, draw_object_func, show_vector); - } - } break; - case DRAW_TASK_OBJECT_TRANSLUCENT: { - for (uint32_t j = 0; j < task->data.object_translucent.count; j++) { - draw_object* object = task->data.object_translucent.objects[j]; - int32_t a = (int32_t)(object->blend_color.w * 255.0f); - a = clamp_def(a, 0, 255); - if (a == alpha) - draw_object_draw(rctx, object, - &task->mat, draw_object_func, show_vector); - } - } break; - } - } - - switch (type) { - case DRAW_OBJECT_TRANSLUCENT: - case DRAW_OBJECT_TRANSLUCENT_NO_SHADOW: - case DRAW_OBJECT_REFLECT_TRANSLUCENT: - case DRAW_OBJECT_REFRACT_TRANSLUCENT: - case DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_1: - case DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_2: - case DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_3: - if (!a2) - gl_state_set_depth_mask(GL_TRUE); - break; - case DRAW_OBJECT_TYPE_6: - case DRAW_OBJECT_TYPE_7: - rctx->draw_state.shader_index = -1; - gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); - break; - case DRAW_OBJECT_REFLECT_CHARA_OPAQUE: - case DRAW_OBJECT_REFLECT_CHARA_TRANSLUCENT: - case DRAW_OBJECT_REFLECT_CHARA_TRANSPARENT: - case DRAW_OBJECT_TRANSPARENT_ALPHA_ORDER_1: - case DRAW_OBJECT_TRANSPARENT_ALPHA_ORDER_2: - case DRAW_OBJECT_TRANSPARENT_ALPHA_ORDER_3: - gl_state_set_cull_face_mode(GL_BACK); - break; - } - - uniform_value_reset(); - shader::unbind(); - gl_state_set_blend_func(GL_ONE, GL_ZERO); -} - -void draw_task_draw_objects_by_type_translucent(render_context* rctx, bool opaque_enable, - bool transparent_enable, bool translucent_enable, draw_object_type opaque, - draw_object_type transparent, draw_object_type translucent) { - if (draw_task_get_count(rctx, opaque) < 1 - && draw_task_get_count(rctx, transparent) < 1 - && draw_task_get_count(rctx, translucent) < 1) - return; - - post_process* pp = &rctx->post_process; - - int32_t alpha_array[256]; - int32_t count = draw_task_translucent_sort_count_layers(rctx, - alpha_array, opaque, transparent, translucent); - for (int32_t i = 0; i < count; i++) { - int32_t alpha = alpha_array[i]; - fbo::blit(pp->rend_texture.fbos[0], pp->alpha_layer_texture.fbos[0], - 0, 0, pp->render_width, pp->render_height, - 0, 0, pp->render_width, pp->render_height, - GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST); - - if (opaque_enable && draw_task_get_count(rctx, opaque)) - draw_task_draw_objects_by_type(rctx, opaque, 0, 0, true, alpha); - if (transparent_enable && draw_task_get_count(rctx, transparent)) - draw_task_draw_objects_by_type(rctx, transparent, 0, 0, true, alpha); - if (translucent_enable && draw_task_get_count(rctx, translucent)) { - gl_state_enable_blend(); - draw_task_draw_objects_by_type(rctx, translucent, 0, 0, true, alpha); - gl_state_disable_blend(); - } - - pp->buf_texture.bind(); - pp->alpha_layer_shader.use(); - gl_state_active_bind_texture_2d(0, pp->alpha_layer_texture.color_texture->tex); - gl_state_active_bind_texture_2d(1, pp->rend_texture.color_texture->tex); - glUniform1f(0, (float_t)(alpha * (1.0 / 255.0))); - render_texture::draw_custom_glsl(); - - fbo::blit(pp->buf_texture.fbos[0], pp->rend_texture.fbos[0], - 0, 0, pp->render_width, pp->render_height, - 0, 0, pp->render_width, pp->render_height, - GL_COLOR_BUFFER_BIT, GL_NEAREST); - } -} - -inline int32_t draw_task_get_count(render_context* rctx, draw_object_type type) { - return (int32_t)rctx->object_data.draw_task_array[type].size(); -} - -bool draw_task_add_draw_object(render_context* rctx, obj* object, - obj_mesh_vertex_buffer* obj_vertex_buf, obj_mesh_index_buffer* obj_index_buf, const mat4* mat, - std::vector* textures, vec4* blend_color, mat4* bone_mat, obj* object_morph, - obj_mesh_vertex_buffer* obj_morph_vertex_buf, int32_t instances_count, - mat4* instances_mat, void(*draw_object_func)(draw_object*), bool enable_bone_mat, bool local) { - object_data* object_data = &rctx->object_data; - draw_task_flags draw_task_flags = object_data->draw_task_flags; - - rctx->camera->update_data(); - - if (!local && object_data->object_culling && !instances_count && !bone_mat && (!object - || !object_bounding_sphere_check_visibility(&object->bounding_sphere, object_data, rctx->camera, mat))) { - object_data->culled.objects++; - return false; - } - object_data->passed.objects++; - - for (uint32_t i = 0; i < object->num_mesh; i++) { - obj_mesh* mesh = &object->mesh_array[i]; - obj_mesh* mesh_morph = 0; - if (obj_vertex_buf && obj_morph_vertex_buf) { - if (obj_vertex_buf[i].get_size() != obj_morph_vertex_buf[i].get_size()) - continue; - - if (object_morph && i < object_morph->num_mesh) - mesh_morph = &object_morph->mesh_array[i]; - } - - if (!local && object_data->object_culling && !instances_count && !bone_mat - && !object_bounding_sphere_check_visibility(&mesh->bounding_sphere, object_data, rctx->camera, mat) - && (!mesh_morph || !object_bounding_sphere_check_visibility( - &mesh_morph->bounding_sphere, object_data, rctx->camera, mat))) { - object_data->culled.meshes++; - continue; - } - object_data->passed.meshes++; - - draw_object* translucent_priority[40]; - int32_t translucent_priority_count = 0; - - for (uint32_t j = 0; j < mesh->num_submesh; j++) { - obj_sub_mesh* sub_mesh = &mesh->submesh_array[j]; - obj_sub_mesh* sub_mesh_morph = 0; - if (sub_mesh->attrib.m.cloth) - continue; - - if (!local && object_data->object_culling && !instances_count && !bone_mat) { - int32_t v32 = object_bounding_sphere_check_visibility( - &sub_mesh->bounding_sphere, object_data, rctx->camera, mat); - if (v32 != 2 || (!mesh->attrib.m.billboard && !mesh->attrib.m.billboard_y_axis)) { - if (v32 == 2) { - if (object_data->object_bounding_sphere_check_func) - v32 = 1; - else - v32 = obj_axis_aligned_bounding_box_check_visibility( - &sub_mesh->axis_aligned_bounding_box, rctx->camera, mat); - } - - if (!v32) { - if (!mesh_morph || j >= mesh_morph->num_submesh) { - object_data->culled.submesh_array++; - continue; - } - - sub_mesh_morph = &mesh_morph->submesh_array[j]; - if (!sub_mesh_morph) { - object_data->culled.submesh_array++; - continue; - } - - v32 = object_bounding_sphere_check_visibility( - &sub_mesh_morph->bounding_sphere, object_data, rctx->camera, mat); - if (v32 == 2) { - if (object_data->object_bounding_sphere_check_func) - v32 = 1; - else - v32 = obj_axis_aligned_bounding_box_check_visibility( - &sub_mesh_morph->axis_aligned_bounding_box, rctx->camera, mat); - } - - if (!v32) { - object_data->culled.submesh_array++; - continue; - } - } - } - } - object_data->passed.submesh_array++; - - int32_t num_bone_index = sub_mesh->num_bone_index; - uint16_t* bone_index = sub_mesh->bone_index_array; - - mat4* mats; - if (num_bone_index && enable_bone_mat) { - mats = object_data->buffer.add_mat4(num_bone_index); - if (bone_mat) - for (int32_t k = 0; k < num_bone_index; k++, bone_index++) - mats[k] = bone_mat[*bone_index]; - else - for (int32_t k = 0; k < num_bone_index; k++) - mats[k] = mat4_identity; - } - else { - mats = 0; - num_bone_index = 0; - } - - obj_material_data* material = &object->material_array[sub_mesh->material_index]; - draw_task* task = object_data->buffer.add_draw_task(DRAW_TASK_OBJECT); - if (!task) - continue; - - GLuint morph_array_buffer = 0; - if (obj_morph_vertex_buf) - morph_array_buffer = obj_morph_vertex_buf[i].get_buffer(); - - GLuint element_array_buffer = 0; - if (obj_index_buf) - element_array_buffer = obj_index_buf[i].buffer; - - GLuint array_buffer = 0; - if (obj_vertex_buf) - array_buffer = obj_vertex_buf[i].get_buffer(); - - uint32_t material_hash = hash_utf8_murmurhash(material->material.name); - - material_list_struct* mat_list = 0; - for (int32_t k = 0; k < object_data->material_list_count; k++) - if (object_data->material_list_array[k].hash == material_hash) { - mat_list = &object_data->material_list_array[k]; - break; - } - - vec4 _blend_color = 1.0f; - vec4 _emission = 0.0f; - - if (mat_list) { - bool has_blend_color = false; - if (mat_list->has_blend_color.x) { - _blend_color.x = mat_list->blend_color.x; - has_blend_color = true; - } - - if (mat_list->has_blend_color.y) { - _blend_color.y = mat_list->blend_color.y; - has_blend_color = true; - } - - if (mat_list->has_blend_color.z) { - _blend_color.z = mat_list->blend_color.z; - has_blend_color = true; - } - - if (mat_list->has_blend_color.w) { - _blend_color.w = mat_list->blend_color.w; - has_blend_color = true; - } - - if (blend_color) { - if (!has_blend_color) - _blend_color = *blend_color; - else - _blend_color *= *blend_color; - } - - bool has_emission = false; - if (mat_list->has_emission.x) { - _emission.x = mat_list->emission.x; - has_emission = true; - } - - if (mat_list->has_emission.y) { - _emission.y = mat_list->emission.y; - has_emission = true; - } - - if (mat_list->has_emission.z) { - _emission.z = mat_list->emission.z; - has_emission = true; - } - - if (mat_list->has_emission.w) { - _emission.w = mat_list->emission.w; - has_emission = true; - } - - if (!has_emission) - _emission = material->material.color.emission; - } - else { - if (blend_color) - _blend_color = *blend_color; - _emission = material->material.color.emission; - } - - draw_task_object_init(task, object_data, mat, object->bounding_sphere.radius, sub_mesh, - mesh, material, textures, num_bone_index, mats, array_buffer, element_array_buffer, - &_blend_color, &_emission, morph_array_buffer, instances_count, instances_mat, draw_object_func); - - if (draw_task_flags & DRAW_TASK_SHADOW_OBJECT) { - draw_task_add(rctx, (draw_object_type)(DRAW_OBJECT_SHADOW_OBJECT_CHARA - + object_data->shadow_type), task); - if (draw_task_flags & DRAW_TASK_PREPROCESS) - draw_task_add(rctx, DRAW_OBJECT_PREPROCESS, task); - continue; - } - - obj_material_attrib_member attrib = material->material.attrib.m; - if (draw_task_flags & (DRAW_TASK_ALPHA_ORDER_1 | DRAW_TASK_ALPHA_ORDER_2 | DRAW_TASK_ALPHA_ORDER_3) - && task->data.object.blend_color.w < 1.0f) { - if (!(draw_task_flags & DRAW_TASK_NO_TRANSLUCENCY)) { - if (attrib.flag_28 || (attrib.punch_through - || !(attrib.alpha_texture | attrib.alpha_material)) - && !sub_mesh->attrib.m.transparent) { - if (!attrib.punch_through) { - if (draw_task_flags & DRAW_TASK_ALPHA_ORDER_1) - draw_task_add(rctx, DRAW_OBJECT_OPAQUE_ALPHA_ORDER_1, task); - else if (draw_task_flags & DRAW_TASK_ALPHA_ORDER_2) - draw_task_add(rctx, local ? DRAW_OBJECT_OPAQUE_ALPHA_ORDER_2_LOCAL - : DRAW_OBJECT_OPAQUE_ALPHA_ORDER_2, task); - else - draw_task_add(rctx, DRAW_OBJECT_OPAQUE_ALPHA_ORDER_3, task); - } - else { - if (draw_task_flags & DRAW_TASK_ALPHA_ORDER_1) - draw_task_add(rctx, DRAW_OBJECT_TRANSPARENT_ALPHA_ORDER_1, task); - else if (draw_task_flags & DRAW_TASK_ALPHA_ORDER_2) - draw_task_add(rctx, local ? DRAW_OBJECT_TRANSPARENT_ALPHA_ORDER_2_LOCAL - : DRAW_OBJECT_TRANSPARENT_ALPHA_ORDER_2, task); - else - draw_task_add(rctx, DRAW_OBJECT_TRANSPARENT_ALPHA_ORDER_3, task); - } - - if (draw_task_flags & DRAW_TASK_SSS) - draw_task_add(rctx, DRAW_OBJECT_SSS, task); - } - - if (draw_task_flags & DRAW_TASK_ALPHA_ORDER_1) - draw_task_add(rctx, DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_1, task); - else if (draw_task_flags & DRAW_TASK_ALPHA_ORDER_2) - draw_task_add(rctx, local ? DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_2_LOCAL - : DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_2, task); - else - draw_task_add(rctx, DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_3, task); - } - } - else { - if (attrib.flag_28 || task->data.object.blend_color.w >= 1.0f - && (attrib.punch_through || !(attrib.alpha_texture | attrib.alpha_material)) - && !sub_mesh->attrib.m.transparent) { - if (draw_task_flags & DRAW_TASK_SHADOW) - draw_task_add(rctx, (draw_object_type)(DRAW_OBJECT_SHADOW_CHARA - + object_data->shadow_type), task); - - if (draw_task_flags & DRAW_TASK_SSS) - draw_task_add(rctx, DRAW_OBJECT_SSS, task); - - if (attrib.punch_through) { - if (!(draw_task_flags & DRAW_TASK_NO_TRANSLUCENCY)) - draw_task_add(rctx, local ? DRAW_OBJECT_TRANSPARENT_LOCAL - : DRAW_OBJECT_TRANSPARENT, task); - - if (draw_task_flags & DRAW_TASK_CHARA_REFLECT) - draw_task_add(rctx, DRAW_OBJECT_REFLECT_CHARA_OPAQUE, task); - - if (draw_task_flags & DRAW_TASK_REFLECT) - draw_task_add(rctx, DRAW_OBJECT_REFLECT_OPAQUE, task); - - if (draw_task_flags & DRAW_TASK_REFRACT) - draw_task_add(rctx, DRAW_OBJECT_REFRACT_TRANSPARENT, task); - } - else { - if (!(draw_task_flags & DRAW_TASK_NO_TRANSLUCENCY)) - draw_task_add(rctx, local ? DRAW_OBJECT_OPAQUE_LOCAL - : DRAW_OBJECT_OPAQUE, task); - - if (draw_task_flags & DRAW_TASK_20) - draw_task_add(rctx, DRAW_OBJECT_TYPE_6, task); - - if (draw_task_flags & DRAW_TASK_CHARA_REFLECT) - draw_task_add(rctx, DRAW_OBJECT_REFLECT_CHARA_OPAQUE, task); - - if (draw_task_flags & DRAW_TASK_REFLECT) - draw_task_add(rctx, DRAW_OBJECT_REFLECT_OPAQUE, task); - - if (draw_task_flags & DRAW_TASK_REFRACT) - draw_task_add(rctx, DRAW_OBJECT_REFRACT_OPAQUE, task); - } - - if (draw_task_flags & DRAW_TASK_PREPROCESS) - draw_task_add(rctx, DRAW_OBJECT_PREPROCESS, task); - continue; - } - else if (!(draw_task_flags & DRAW_TASK_NO_TRANSLUCENCY)) { - if (!attrib.translucent_priority) - if (local) - draw_task_add(rctx, DRAW_OBJECT_TRANSLUCENT_LOCAL, task); - else if (mesh->attrib.m.translucent_no_shadow - || draw_task_flags & DRAW_TASK_TRANSLUCENT_NO_SHADOW) - draw_task_add(rctx, DRAW_OBJECT_TRANSLUCENT_NO_SHADOW, task); - else - draw_task_add(rctx, DRAW_OBJECT_TRANSLUCENT, task); - else if (translucent_priority_count < 40) - translucent_priority[translucent_priority_count++] = &task->data.object; - } - } - - if (draw_task_flags & DRAW_TASK_SHADOW) - draw_task_add(rctx, (draw_object_type)(DRAW_OBJECT_SHADOW_CHARA - + object_data->shadow_type), task); - if (draw_task_flags & DRAW_TASK_40) - draw_task_add(rctx, DRAW_OBJECT_TYPE_7, task); - if (draw_task_flags & DRAW_TASK_CHARA_REFLECT) - draw_task_add(rctx, DRAW_OBJECT_REFLECT_CHARA_OPAQUE, task); - if (draw_task_flags & DRAW_TASK_REFLECT) { - if (rctx->draw_pass.reflect_type != STAGE_DATA_REFLECT_REFLECT_MAP) - draw_task_add(rctx, DRAW_OBJECT_REFLECT_OPAQUE, task); - else - draw_task_add(rctx, DRAW_OBJECT_REFLECT_TRANSLUCENT, task); - } - if (draw_task_flags & DRAW_TASK_REFRACT) - draw_task_add(rctx, DRAW_OBJECT_REFRACT_TRANSLUCENT, task); - if (draw_task_flags & DRAW_TASK_PREPROCESS) - draw_task_add(rctx, DRAW_OBJECT_PREPROCESS, task); - } - - if (!translucent_priority_count) - continue; - - draw_task_object_translucent object_translucent; - object_translucent.count = 0; - for (int32_t j = 62; j; j--) - for (int32_t k = 0; k < translucent_priority_count; k++) { - draw_object* object = translucent_priority[k]; - if (object->material->material.attrib.m.translucent_priority != j) - continue; - - object_translucent.objects[object_translucent.count] = object; - object_translucent.count++; - } - - draw_task* task = rctx->object_data.buffer.add_draw_task(DRAW_TASK_OBJECT_TRANSLUCENT); - if (!task) - continue; - - draw_task_object_translucent_init(task, mat, &object_translucent); - if (draw_task_flags & DRAW_TASK_ALPHA_ORDER_1) - draw_task_add(rctx, DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_1, task); - else if (draw_task_flags & DRAW_TASK_ALPHA_ORDER_2) - draw_task_add(rctx, local ? DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_2_LOCAL - : DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_2, task); - else if (draw_task_flags & DRAW_TASK_ALPHA_ORDER_3) - draw_task_add(rctx, DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_3, task); - else - draw_task_add(rctx, local ? DRAW_OBJECT_TRANSLUCENT_LOCAL : DRAW_OBJECT_TRANSLUCENT, task); - } - return true; -} - -inline void draw_task_add_draw_object_by_obj(render_context* rctx, const mat4* mat, - obj* obj, std::vector* textures, obj_mesh_vertex_buffer* obj_vert_buf, - obj_mesh_index_buffer* obj_index_buf, mat4* bone_mat, float_t alpha) { - if (!obj) - return; - - vec4 blend_color = { 1.0f, 1.0f, 1.0f, alpha }; - vec4* blend_color_ptr = alpha < 1.0f ? &blend_color : 0; - - draw_task_add_draw_object(rctx, obj, obj_vert_buf, obj_index_buf, mat, textures, blend_color_ptr, - bone_mat, 0, 0, 0, 0, 0, !!bone_mat); -} - -inline bool draw_task_add_draw_object_by_object_info(render_context* rctx, const mat4* mat, - object_info obj_info, mat4* bone_mat) { - vec4 blend_color = 1.0f; - return draw_task_add_draw_object_by_object_info(rctx, - mat, obj_info, &blend_color, bone_mat, 0, 0, 0, true); -} - -bool draw_task_add_draw_object_by_object_info(render_context* rctx, const mat4* mat, - object_info obj_info, vec4* blend_color, mat4* bone_mat, int32_t instances_count, - mat4* instances_mat, void(*draw_object_func)(draw_object*), bool enable_bone_mat, bool local) { - if (obj_info.id == -1 && obj_info.set_id == -1) - return false; - - obj* object = object_storage_get_obj(obj_info); - if (!object) - return false; - - std::vector* textures = object_storage_get_obj_set_textures(obj_info.set_id); - obj_mesh_vertex_buffer* obj_vertex_buffer = object_storage_get_obj_mesh_vertex_buffer(obj_info); - obj_mesh_index_buffer* obj_index_buffer = object_storage_get_obj_mesh_index_buffer(obj_info); - - obj* obj_morph = 0; - obj_mesh_vertex_buffer* obj_morph_vertex_buffer = 0; - object_data* object_data = &rctx->object_data; - if (object_data->morph.object.set_id != -1) { - obj_morph = object_storage_get_obj(object_data->morph.object); - obj_morph_vertex_buffer = object_storage_get_obj_mesh_vertex_buffer(object_data->morph.object); - } - - return draw_task_add_draw_object(rctx, object, obj_vertex_buffer, obj_index_buffer, - mat, textures, blend_color, bone_mat, obj_morph, obj_morph_vertex_buffer, - instances_count, instances_mat, draw_object_func, enable_bone_mat, local); -} - -inline bool draw_task_add_draw_object_by_object_info(render_context* rctx, const mat4* mat, - object_info obj_info, float_t alpha, mat4* bone_mat) { - vec4 blend_color = 1.0f; - blend_color.w = alpha; - return draw_task_add_draw_object_by_object_info(rctx, - mat, obj_info, &blend_color, bone_mat, 0, 0, 0, true); -} - -inline bool draw_task_add_draw_object_by_object_info(render_context* rctx, const mat4* mat, - object_info obj_info, float_t r, float_t g, float_t b, float_t a, mat4* bone_mat, bool local) { - vec4 blend_color = { r, g, b, a }; - return draw_task_add_draw_object_by_object_info(rctx, - mat, obj_info, &blend_color, bone_mat, 0, 0, 0, true, local); -} - -inline bool draw_task_add_draw_object_by_object_info(render_context* rctx, const mat4* mat, - object_info obj_info, vec4* blend_color, mat4* bone_mat, bool local) { - return draw_task_add_draw_object_by_object_info(rctx, - mat, obj_info, blend_color, 0, 0, 0, 0, false, local); -} - -void draw_task_add_draw_object_by_object_info_object_skin(render_context* rctx, object_info obj_info, - std::vector* texture_pattern, texture_data_struct* texture_data, float_t alpha, - mat4* matrices, mat4* ex_data_matrices, const mat4* mat, mat4* global_mat) { - obj_skin* skin = object_storage_get_obj_skin(obj_info); - if (!skin) - return; - - obj_skin_set_matrix_buffer(skin, matrices, ex_data_matrices, rctx->matrix_buffer, mat, global_mat); - - vec4 texture_color_coeff; - vec4 texture_color_offset; - vec4 texture_specular_coeff; - vec4 texture_specular_offset; - object_data* object_data = &rctx->object_data; - if (texture_data && !texture_data->field_0) { - vec4 value; - object_data->get_texture_color_coeff(texture_color_coeff); - *(vec3*)&value = texture_data->texture_color_coeff * *(vec3*)&texture_color_coeff; - value.w = 0.0f; - object_data->set_texture_color_coeff(value); - - object_data->get_texture_color_offset(texture_color_offset); - *(vec3*)&value = texture_data->texture_color_offset; - value.w = 0.0f; - object_data->set_texture_color_offset(value); - - object_data->get_texture_specular_coeff(texture_specular_coeff); - *(vec3*)&value = texture_data->texture_specular_coeff * *(vec3*)&texture_specular_coeff; - value.w = 0.0f; - object_data->set_texture_specular_coeff(value); - - object_data->get_texture_specular_offset(texture_specular_offset); - *(vec3*)&value = texture_data->texture_specular_offset; - value.w = 0.0f; - object_data->set_texture_specular_offset(value); - } - - size_t texture_pattern_count = texture_pattern ? texture_pattern->size() : 0; - if (texture_pattern && texture_pattern_count) - object_data->set_texture_pattern((int32_t)texture_pattern_count, texture_pattern->data()); - - if (fabsf(alpha - 1.0f) > 0.000001f) - draw_task_add_draw_object_by_object_info(rctx, - global_mat, obj_info, alpha, rctx->matrix_buffer); - else - draw_task_add_draw_object_by_object_info(rctx, - global_mat, obj_info, rctx->matrix_buffer); - - if (texture_pattern && texture_pattern_count) - object_data->set_texture_pattern(); - - if (texture_data && !texture_data->field_0) { - object_data->set_texture_color_coeff(texture_color_coeff); - object_data->set_texture_color_offset(texture_color_offset); - object_data->set_texture_specular_coeff(texture_specular_coeff); - object_data->set_texture_specular_offset(texture_specular_offset); - } -} - -void draw_task_add_draw_preprocess(render_context* rctx, const mat4* mat, - void(*func)(render_context* rctx, void* data), void* data, draw_object_type type) { - object_data* object_data = &rctx->object_data; - draw_task* task = object_data->buffer.add_draw_task(DRAW_TASK_OBJECT_PREPROCESS); - if (task) { - draw_task_preprocess_init(task, mat, func, data); - draw_task_add(rctx, type, task); - } -} - -void draw_task_sort(render_context* rctx, draw_object_type type, int32_t compare_func) { - std::vector& vec = rctx->object_data.draw_task_array[type]; - if (vec.size() < 1) - return; - - object_data* object_data = &rctx->object_data; - for (draw_task*& i : vec) { - draw_task* task = i; - vec3 center; - mat4_get_translation(&task->mat, ¢er); - if (task->type == DRAW_TASK_OBJECT) { - mat4 mat = task->mat; - if (task->data.object.mesh->attrib.m.billboard) - model_mat_face_camera_view(&rctx->camera->view, &mat, &mat); - else if (task->data.object.mesh->attrib.m.billboard_y_axis) - model_mat_face_camera_position(&rctx->camera->view, &mat, &mat); - - obj_sub_mesh* sub_mesh = task->data.object.sub_mesh; - if (task->data.object.mat_count < 1 || !sub_mesh->num_bone_index) - mat4_mult_vec3_trans(&mat, &sub_mesh->bounding_sphere.center, ¢er); - else { - vec3 center_sum = 0.0f; - for (uint32_t j = 0; j < sub_mesh->num_bone_index; j++) { - center = sub_mesh->bounding_sphere.center; - mat4_mult_vec3_trans(&task->data.object.mats[j], ¢er, ¢er); - center_sum += center; - } - center_sum *= 1.0f / (float_t)sub_mesh->num_bone_index; - } - task->bounding_radius = task->data.object.mesh->bounding_sphere.radius; - } - - mat4_mult_vec3_trans(&rctx->camera->view, ¢er, ¢er); - task->depth = center.z; - } - - switch (compare_func) { - case 0: - quicksort_custom(vec.data(), vec.size(), - sizeof(draw_task*), draw_task_sort_quicksort_compare0); - break; - case 1: - quicksort_custom(vec.data(), vec.size(), - sizeof(draw_task*), draw_task_sort_quicksort_compare1); - break; - case 2: - quicksort_custom(vec.data(), vec.size(), - sizeof(draw_task*), draw_task_sort_quicksort_compare2); - break; - } -} - -int32_t obj_axis_aligned_bounding_box_check_visibility( - obj_axis_aligned_bounding_box* aabb, camera* cam, const mat4* mat) { - vec3 points[8]; - points[0] = aabb->center + (aabb->size ^ vec3( 0.0f, 0.0f, 0.0f)); - points[1] = aabb->center + (aabb->size ^ vec3(-0.0f, -0.0f, -0.0f)); - points[2] = aabb->center + (aabb->size ^ vec3(-0.0f, 0.0f, 0.0f)); - points[3] = aabb->center + (aabb->size ^ vec3( 0.0f, -0.0f, -0.0f)); - points[4] = aabb->center + (aabb->size ^ vec3( 0.0f, -0.0f, 0.0f)); - points[5] = aabb->center + (aabb->size ^ vec3(-0.0f, 0.0f, -0.0f)); - points[6] = aabb->center + (aabb->size ^ vec3( 0.0f, 0.0f, -0.0f)); - points[7] = aabb->center + (aabb->size ^ vec3(-0.0f, -0.0f, 0.0f)); - - mat4 view_mat; - mat4_mult(mat, &cam->view, &view_mat); - for (int32_t i = 0; i < 8; i++) - mat4_mult_vec3_trans(&view_mat, &points[i], &points[i]); - - vec4 v2[6]; - *(vec3*)&v2[0] = { 0.0f, 0.0f, -1.0f }; - v2[0].w = (float_t)-cam->min_distance; - *(vec3*)&v2[1] = cam->field_1E4; - v2[1].w = 0.0f; - *(vec3*)&v2[2] = cam->field_1F0; - v2[2].w = 0.0f; - *(vec3*)&v2[3] = cam->field_1FC; - v2[3].w = 0.0f; - *(vec3*)&v2[4] = cam->field_208; - v2[4].w = 0.0f; - *(vec3*)&v2[5] = { 0.0f, 0.0f, 1.0f }; - v2[5].w = (float_t)cam->max_distance; - - for (int32_t i = 0; i < 6; i++) - for (int32_t j = 0; j < 8; j++) { - float_t v34 = vec3::dot(*(vec3*)&v2[i], points[j]) + v2[i].w; - if (v34 > 0.0f) - break; - - if (j == 7) - return 0; - } - return 1; -} - -int32_t object_bounding_sphere_check_visibility(obj_bounding_sphere* sphere, - object_data* object_data, camera* cam, const mat4* mat) { - if (object_data->object_bounding_sphere_check_func) - return object_data->object_bounding_sphere_check_func(sphere, cam); - - vec3 center; - mat4_mult_vec3_trans(mat, &sphere->center, ¢er); - mat4_mult_vec3_trans(&cam->view, ¢er, ¢er); - float_t radius = mat4_get_max_scale(mat) * sphere->radius; - - double_t min_depth = (double_t)center.z - (double_t)radius; - double_t max_depth = (double_t)center.z + (double_t)radius; - if (-cam->min_distance < min_depth || -cam->max_distance > max_depth) - return 0; - - float_t v5 = vec3::dot(cam->field_1E4, center); - if (v5 < -radius) - return 0; - - float_t v6 = vec3::dot(cam->field_1F0, center); - if (v6 < -radius) - return 0; - - float_t v7 = vec3::dot(cam->field_1FC, center); - if (v7 < -radius) - return 0; - - float_t v8 = vec3::dot(cam->field_208, center); - if (v8 < -radius) - return 0; - - if (-cam->min_distance >= max_depth && -cam->max_distance <= min_depth - && v5 >= radius && v6 >= radius && v7 >= radius && v8 >= radius) - return 1; - return 2; -} - -inline static void draw_task_add(render_context* rctx, draw_object_type type, draw_task* task) { - rctx->object_data.draw_task_array[type].push_back(task); -} - -inline static void draw_task_preprocess_init(draw_task* task, const mat4* mat, - void(*func)(render_context* rctx, void* data), void* data) { - task->type = DRAW_TASK_OBJECT_PREPROCESS; - task->mat = *mat; - task->data.preprocess.func = func; - task->data.preprocess.data = data; -} - -inline static void draw_task_object_init(draw_task* task, object_data* object_data, const mat4* mat, - float_t bounding_radius, obj_sub_mesh* sub_mesh, obj_mesh* mesh, obj_material_data* material, - std::vector* textures, int32_t mat_count, mat4* mats, GLuint array_buffer, - GLuint element_array_buffer, vec4* blend_color, vec4* emission, int32_t morph_array_buffer, - int32_t instances_count, mat4* instances_mat, void(*draw_object_func)(draw_object*)) { - task->type = DRAW_TASK_OBJECT; - task->mat = *mat; - task->bounding_radius = bounding_radius; - task->data.object.mesh = mesh; - task->data.object.morph_value = object_data->morph.value; - task->data.object.material = material; - task->data.object.sub_mesh = sub_mesh; - task->data.object.textures = textures; - task->data.object.mat_count = mat_count; - task->data.object.mats = mats; - task->data.object.array_buffer = array_buffer; - task->data.object.element_array_buffer = element_array_buffer; - task->data.object.morph_array_buffer = morph_array_buffer; - - draw_object* draw = &task->data.object; - draw->texture_pattern_count = object_data->texture_pattern_count; - for (int32_t i = 0; i < object_data->texture_pattern_count && i < TEXTURE_PATTERN_COUNT; i++) - draw->texture_pattern_array[i] = object_data->texture_pattern_array[i]; - - draw->texture_transform_count = object_data->texture_transform_count; - for (int32_t i = 0; i < object_data->texture_transform_count && i < TEXTURE_TRANSFORM_COUNT; i++) - draw->texture_transform_array[i] = object_data->texture_transform_array[i]; - - if (blend_color && *blend_color != 1.0f) { - draw->set_blend_color = true; - draw->blend_color = *blend_color; - } - else { - draw->set_blend_color = false; - draw->blend_color = 1.0f; - } - - draw->emission = *emission; - - draw->chara_color = object_data->chara_color; - draw->self_shadow = object_data->draw_task_flags & (DRAW_TASK_8 | DRAW_TASK_4) ? 1 : 0; - draw->shadow = object_data->shadow_type; - draw->texture_color_coeff = object_data->texture_color_coeff; - draw->texture_color_coeff.w = object_data->wet_param; - draw->texture_color_offset = object_data->texture_color_offset; - draw->texture_specular_coeff = object_data->texture_specular_coeff; - draw->texture_specular_offset = object_data->texture_specular_offset; - draw->instances_count = instances_count; - draw->instances_mat = instances_mat; - draw->draw_object_func = draw_object_func; - - object_data->add_vertex_array(&task->data.object); -} - -inline static void draw_task_object_translucent_init(draw_task* task, const mat4* mat, - draw_task_object_translucent* object) { - task->type = DRAW_TASK_OBJECT_TRANSLUCENT; - task->mat = *mat; - task->data.object_translucent = *object; -} - -static int draw_task_sort_quicksort_compare0(void const* src1, void const* src2) { - float_t d1 = (*(draw_task**)src1)->depth; - float_t d2 = (*(draw_task**)src2)->depth; - return d1 > d2 ? -1 : (d1 < d2 ? 1 : 0); -} - -static int draw_task_sort_quicksort_compare1(void const* src1, void const* src2) { - float_t d1 = (*(draw_task**)src1)->depth; - float_t d2 = (*(draw_task**)src2)->depth; - return d1 < d2 ? -1 : (d1 > d2 ? 1 : 0); -} - -static int draw_task_sort_quicksort_compare2(void const* src1, void const* src2) { - float_t r1 = (*(draw_task**)src1)->bounding_radius; - float_t r2 = (*(draw_task**)src2)->bounding_radius; - return r1 > r2 ? -1 : (r1 < r2 ? 1 : 0); -} - -inline static int32_t draw_task_translucent_sort_count_layers(render_context* rctx, - int32_t* alpha_array, draw_object_type opaque, - draw_object_type transparent, draw_object_type translucent) { - bool arr[256] = { 0 }; - - draw_task_translucent_sort_has_objects(rctx, arr, opaque); - draw_task_translucent_sort_has_objects(rctx, arr, transparent); - draw_task_translucent_sort_has_objects(rctx, arr, translucent); - - int32_t count = 0; - for (int32_t i = 0xFF; i >= 1; i--) - if (arr[i]) { - count++; - *alpha_array++ = i; - } - return count; -} - -inline static void draw_task_translucent_sort_has_objects(render_context* rctx, bool* arr, draw_object_type type) { - std::vector& vec = rctx->object_data.draw_task_array[type]; - for (draw_task*& i : vec) { - draw_task* task = i; - if (task->type != DRAW_TASK_OBJECT) - continue; - - int32_t alpha = (int32_t)(task->data.object.blend_color.w * 255.0f); - alpha = clamp_def(alpha, 0, 255); - arr[alpha] = true; - } -} diff --git a/src/CRE/draw_task.hpp b/src/CRE/draw_task.hpp deleted file mode 100644 index 0559b328..00000000 --- a/src/CRE/draw_task.hpp +++ /dev/null @@ -1,48 +0,0 @@ -/* - by korenkonder - GitHub/GitLab: korenkonder -*/ - -#pragma once - -#include "../KKdLib/default.hpp" -#include "../KKdLib/mat.hpp" -#include "../KKdLib/vec.hpp" -#include "draw_object.hpp" -#include "draw_primitive.hpp" - -extern void draw_task_draw_objects_by_type(render_context* rctx, draw_object_type type, - int32_t a2 = 0, int32_t a3 = 0, bool a4 = true, int32_t alpha = -1); -extern void draw_task_draw_objects_by_type_translucent(render_context* rctx, bool opaque_enable, - bool transparent_enable, bool translucent_enable, draw_object_type opaque, - draw_object_type transparent, draw_object_type translucent); -extern int32_t draw_task_get_count(render_context* rctx, draw_object_type type); -extern bool draw_task_add_draw_object(render_context* rctx, obj* object, - obj_mesh_vertex_buffer* obj_vertex_buf, obj_mesh_index_buffer* obj_index_buf, const mat4* mat, - std::vector* textures, vec4* blend_color, mat4* bone_mat, obj* object_morph, - obj_mesh_vertex_buffer* obj_morph_vertex_buf, int32_t instances_count, - mat4* instances_mat, void(*draw_object_func)(draw_object*), bool enable_bone_mat, bool local = false); -extern void draw_task_add_draw_object_by_obj(render_context* rctx, const mat4* mat, - obj* obj, std::vector* textures, obj_mesh_vertex_buffer* obj_vert_buf, - obj_mesh_index_buffer* obj_index_buf, mat4* bone_mat, float_t alpha); -extern bool draw_task_add_draw_object_by_object_info(render_context* rctx, const mat4* mat, - object_info obj_info, mat4* bone_mat = 0); -extern bool draw_task_add_draw_object_by_object_info(render_context* rctx, const mat4* mat, - object_info obj_info, vec4* blend_color, mat4* bone_mat, int32_t instances_count, - mat4* instances_mat, void(*draw_object_func)(draw_object*), bool enable_bone_mat, bool local = false); -extern bool draw_task_add_draw_object_by_object_info(render_context* rctx, const mat4* mat, - object_info obj_info, float_t alpha, mat4* bone_mat = 0); -extern bool draw_task_add_draw_object_by_object_info(render_context* rctx, const mat4* mat, - object_info obj_info, float_t r, float_t g, float_t b, float_t a, mat4* bone_mat = 0, bool local = false); -extern bool draw_task_add_draw_object_by_object_info(render_context* rctx, const mat4* mat, - object_info obj_info, vec4* blend_color, mat4* bone_mat = 0, bool local = false); -extern void draw_task_add_draw_object_by_object_info_object_skin(render_context* rctx, object_info obj_info, - std::vector* texture_pattern, texture_data_struct* texture_data, float_t alpha, - mat4* matrices, mat4* ex_data_matrices, const mat4* mat, mat4* global_mat); -extern void draw_task_add_draw_preprocess(render_context* rctx, const mat4* mat, - void(*func)(render_context* rctx, void* data), void* data, draw_object_type type); -extern void draw_task_sort(render_context* rctx, draw_object_type type, int32_t compare_func); -extern int32_t obj_axis_aligned_bounding_box_check_visibility( - obj_axis_aligned_bounding_box* aabb, camera* cam, const mat4* mat); -extern int32_t object_bounding_sphere_check_visibility(obj_bounding_sphere* sphere, - object_data* object_data, camera* cam, const mat4* mat); diff --git a/src/CRE/gl_state.cpp b/src/CRE/gl_state.cpp index 66ec3ae1..d139ddd4 100644 --- a/src/CRE/gl_state.cpp +++ b/src/CRE/gl_state.cpp @@ -84,56 +84,58 @@ void gl_state_bind_element_array_buffer(GLuint buffer, bool force) { } void gl_state_bind_uniform_buffer(GLuint buffer, bool force) { - gl_state_bind_uniform_buffer_base(gl_state.uniform_buffer_index, buffer); + if (gl_state.uniform_buffer_binding != buffer || force) { + glBindBuffer(GL_UNIFORM_BUFFER, buffer); + gl_state.uniform_buffer_binding = buffer; + } } void gl_state_bind_uniform_buffer_base(GLuint index, GLuint buffer, bool force) { - if (gl_state.uniform_buffer_binding[index] != buffer) { + if (gl_state.uniform_buffer_bindings[index] != buffer || force) { glBindBufferBase(GL_UNIFORM_BUFFER, index, buffer); - gl_state.uniform_buffer_binding[index] = buffer; - gl_state.uniform_buffer_offset[index] = 0; - gl_state.uniform_buffer_size[index] = -1; - gl_state.uniform_buffer_index = index; + gl_state.uniform_buffer_bindings[index] = buffer; + gl_state.uniform_buffer_offsets[index] = 0; + gl_state.uniform_buffer_sizes[index] = -1; } } void gl_state_bind_uniform_buffer_range(GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size, bool force) { - if (gl_state.uniform_buffer_binding[index] != buffer - || gl_state.uniform_buffer_offset[index] != offset - || gl_state.uniform_buffer_size[index] != size) { + if (gl_state.uniform_buffer_bindings[index] != buffer + || gl_state.uniform_buffer_offsets[index] != offset + || gl_state.uniform_buffer_sizes[index] != size || force) { glBindBufferRange(GL_UNIFORM_BUFFER, index, buffer, offset, size); - gl_state.uniform_buffer_binding[index] = buffer; - gl_state.uniform_buffer_offset[index] = offset; - gl_state.uniform_buffer_size[index] = size; - gl_state.uniform_buffer_index = index; + gl_state.uniform_buffer_bindings[index] = buffer; + gl_state.uniform_buffer_offsets[index] = offset; + gl_state.uniform_buffer_sizes[index] = size; } } void gl_state_bind_shader_storage_buffer(GLuint buffer, bool force) { - gl_state_bind_shader_storage_buffer_base(gl_state.shader_storage_buffer_index, buffer); + if (gl_state.shader_storage_buffer_binding != buffer || force) { + glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer); + gl_state.shader_storage_buffer_binding = buffer; + } } void gl_state_bind_shader_storage_buffer_base(GLuint index, GLuint buffer, bool force) { - if (gl_state.shader_storage_buffer_binding[index] != buffer) { + if (gl_state.shader_storage_buffer_bindings[index] != buffer || force) { glBindBufferBase(GL_SHADER_STORAGE_BUFFER, index, buffer); - gl_state.shader_storage_buffer_binding[index] = buffer; - gl_state.shader_storage_buffer_offset[index] = 0; - gl_state.shader_storage_buffer_size[index] = -1; - gl_state.shader_storage_buffer_index = index; + gl_state.shader_storage_buffer_bindings[index] = buffer; + gl_state.shader_storage_buffer_offsets[index] = 0; + gl_state.shader_storage_buffer_sizes[index] = -1; } } void gl_state_bind_shader_storage_buffer_range(GLuint index, GLuint buffer, GLintptr offset, GLsizeiptr size, bool force) { - if (gl_state.shader_storage_buffer_binding[index] != buffer - || gl_state.shader_storage_buffer_offset[index] != offset - || gl_state.shader_storage_buffer_size[index] != size) { + if (gl_state.shader_storage_buffer_bindings[index] != buffer + || gl_state.shader_storage_buffer_offsets[index] != offset + || gl_state.shader_storage_buffer_sizes[index] != size || force) { glBindBufferRange(GL_SHADER_STORAGE_BUFFER, index, buffer, offset, size); - gl_state.shader_storage_buffer_binding[index] = buffer; - gl_state.shader_storage_buffer_offset[index] = offset; - gl_state.shader_storage_buffer_size[index] = size; - gl_state.shader_storage_buffer_index = index; + gl_state.shader_storage_buffer_bindings[index] = buffer; + gl_state.shader_storage_buffer_offsets[index] = offset; + gl_state.shader_storage_buffer_sizes[index] = size; } } @@ -159,19 +161,19 @@ void gl_state_bind_sampler(int32_t index, GLuint sampler) { } bool gl_state_check_uniform_buffer_binding() { - return gl_state.uniform_buffer_binding[gl_state.uniform_buffer_index] != 0; + return gl_state.uniform_buffer_binding != 0; } bool gl_state_check_uniform_buffer_binding_base(size_t index) { - return gl_state.uniform_buffer_binding[index] != 0; + return gl_state.uniform_buffer_bindings[index] != 0; } bool gl_state_check_shader_storage_buffer_binding() { - return gl_state.shader_storage_buffer_binding[gl_state.shader_storage_buffer_index] != 0; + return gl_state.shader_storage_buffer_binding != 0; } bool gl_state_check_shader_storage_buffer_binding_base(size_t index) { - return gl_state.shader_storage_buffer_binding[index] != 0; + return gl_state.shader_storage_buffer_bindings[index] != 0; } bool gl_state_check_texture_binding_2d(size_t index) { @@ -310,24 +312,18 @@ void gl_state_get() { glGetIntegerv(GL_ARRAY_BUFFER_BINDING, (GLint*)&gl_state.array_buffer_binding); glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, (GLint*)&gl_state.element_array_buffer_binding); - GLuint uniform_buffer_binding; - glGetIntegerv(GL_UNIFORM_BUFFER_BINDING, (GLint*)&uniform_buffer_binding); + glGetIntegerv(GL_UNIFORM_BUFFER_BINDING, (GLint*)&gl_state.uniform_buffer_binding); for (GLuint i = 0; i < 14; i++) { - glGetIntegeri_v(GL_UNIFORM_BUFFER_BINDING, i, (GLint*)&gl_state.uniform_buffer_binding[i]); - glGetIntegeri_v(GL_UNIFORM_BUFFER_START, i, (GLint*)&gl_state.uniform_buffer_offset[i]); - glGetIntegeri_v(GL_UNIFORM_BUFFER_SIZE, i, (GLint*)&gl_state.uniform_buffer_size[i]); - if (uniform_buffer_binding == gl_state.uniform_buffer_binding[i]) - gl_state.uniform_buffer_index = i; + glGetIntegeri_v(GL_UNIFORM_BUFFER_BINDING, i, (GLint*)&gl_state.uniform_buffer_bindings[i]); + glGetIntegeri_v(GL_UNIFORM_BUFFER_START, i, (GLint*)&gl_state.uniform_buffer_offsets[i]); + glGetIntegeri_v(GL_UNIFORM_BUFFER_SIZE, i, (GLint*)&gl_state.uniform_buffer_sizes[i]); } - GLuint shader_storage_buffer_binding; - glGetIntegerv(GL_SHADER_STORAGE_BUFFER_BINDING, (GLint*)&shader_storage_buffer_binding); + glGetIntegerv(GL_SHADER_STORAGE_BUFFER_BINDING, (GLint*)&gl_state.shader_storage_buffer_binding); for (GLuint i = 0; i < 14; i++) { - glGetIntegeri_v(GL_SHADER_STORAGE_BUFFER_BINDING, i, (GLint*)&gl_state.shader_storage_buffer_binding[i]); - glGetIntegeri_v(GL_SHADER_STORAGE_BUFFER_START, i, (GLint*)&gl_state.shader_storage_buffer_offset[i]); - glGetIntegeri_v(GL_SHADER_STORAGE_BUFFER_SIZE, i, (GLint*)&gl_state.shader_storage_buffer_size[i]); - if (shader_storage_buffer_binding == gl_state.shader_storage_buffer_binding[i]) - gl_state.shader_storage_buffer_index = i; + glGetIntegeri_v(GL_SHADER_STORAGE_BUFFER_BINDING, i, (GLint*)&gl_state.shader_storage_buffer_bindings[i]); + glGetIntegeri_v(GL_SHADER_STORAGE_BUFFER_START, i, (GLint*)&gl_state.shader_storage_buffer_offsets[i]); + glGetIntegeri_v(GL_SHADER_STORAGE_BUFFER_SIZE, i, (GLint*)&gl_state.shader_storage_buffer_sizes[i]); } glGetBooleanv(GL_COLOR_WRITEMASK, gl_state.color_mask); diff --git a/src/CRE/gl_state.hpp b/src/CRE/gl_state.hpp index 9d975085..ff10f648 100644 --- a/src/CRE/gl_state.hpp +++ b/src/CRE/gl_state.hpp @@ -27,14 +27,14 @@ struct gl_state_struct { GLuint vertex_array_binding; GLuint array_buffer_binding; GLuint element_array_buffer_binding; - GLuint uniform_buffer_index; - GLuint uniform_buffer_binding[14]; - GLintptr uniform_buffer_offset[14]; - GLsizeiptr uniform_buffer_size[14]; - GLuint shader_storage_buffer_index; - GLuint shader_storage_buffer_binding[14]; - GLintptr shader_storage_buffer_offset[14]; - GLsizeiptr shader_storage_buffer_size[14]; + GLuint uniform_buffer_binding; + GLuint uniform_buffer_bindings[14]; + GLintptr uniform_buffer_offsets[14]; + GLsizeiptr uniform_buffer_sizes[14]; + GLuint shader_storage_buffer_binding; + GLuint shader_storage_buffer_bindings[14]; + GLintptr shader_storage_buffer_offsets[14]; + GLsizeiptr shader_storage_buffer_sizes[14]; GLboolean color_mask[4]; GLboolean cull_face; GLenum cull_face_mode; diff --git a/src/CRE/hand_item.cpp b/src/CRE/hand_item.cpp new file mode 100644 index 00000000..50eb0633 --- /dev/null +++ b/src/CRE/hand_item.cpp @@ -0,0 +1,223 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#include "hand_item.hpp" +#include "../KKdLib/database/hand_item.hpp" +#include "../KKdLib/io/path.hpp" +#include "../KKdLib/str_utils.hpp" +#include "data.hpp" +#include "file_handler.hpp" +#include "mdata_manager.hpp" +#include + +struct hand_item_handler { + std::list file_handlers; + bool loaded; + std::map, hand_item> hand_items; + + hand_item_handler(); + ~hand_item_handler(); + + void clear(); + const hand_item* get_hand_item(int32_t uid, chara_index chara_index); + int32_t get_hand_item_uid(const char* str); + bool load(); + void parse(p_file_handler* pfhndl); + void read(); +}; + +static void hand_item_load(data_struct* data, + std::map, hand_item>& hand_items, hnd_itm& hnd_itm_file); + +static int32_t mtp_hand_array_get_mottbl_index(const char* str); + +hand_item_handler hand_item_handler_data; + +hand_item::hand_item() : file_size(), hand_mottbl_index(), hand_scale(), uid() { + +} + +hand_item::~hand_item() { + +} + +void hand_item_handler_data_init() { + hand_item_handler_data = {}; +} + +void hand_item_handler_data_clear() { + hand_item_handler_data.clear(); +} + +const hand_item* hand_item_handler_data_get_hand_item(int32_t uid, chara_index chara_index) { + if (uid >= 0) + return hand_item_handler_data.get_hand_item(uid, chara_index); + return 0; +} + +const std::map, hand_item>& hand_item_handler_data_get_hand_items() { + return hand_item_handler_data.hand_items; +} + +int32_t hand_item_handler_data_get_hand_item_uid(const char* str) { + if (str) + return hand_item_handler_data.get_hand_item_uid(str); + return -1; +} + +bool hand_item_handler_data_load() { + return hand_item_handler_data.load(); +} + +void hand_item_handler_data_read() { + hand_item_handler_data.read(); +} + +void hand_item_handler_data_free() { + hand_item_handler_data.clear(); +} + +hand_item_handler::hand_item_handler() : loaded() { + +} + +hand_item_handler::~hand_item_handler() { + +} +void hand_item_handler::clear() { + loaded = false; + hand_items.clear(); + + for (p_file_handler*& i : file_handlers) + if (i) { + delete i; + i = 0; + } + file_handlers.clear(); +} + +const hand_item* hand_item_handler::get_hand_item(int32_t uid, chara_index chara_index) { + auto elem0 = hand_items.find({ uid, chara_index }); + if (elem0 != hand_items.end()) + return &elem0->second; + + auto elem1 = hand_items.find({ uid, CHARA_NONE }); + if (elem1 != hand_items.end()) + return &elem1->second; + return 0; +} + +int32_t hand_item_handler::get_hand_item_uid(const char* str) { + if (str && hand_items.size()) + for (auto& i : hand_items) + if (!i.second.item_str.compare(str)) + return i.first.first; + return -1; +} + +bool hand_item_handler::load() { + if (loaded) + return false; + + for (p_file_handler*& i : file_handlers) + if (i->check_not_ready()) + return true; + + for (p_file_handler*& i : file_handlers) { + if (!i) + continue; + + parse(i); + + delete i; + i = 0; + } + file_handlers.clear(); + + loaded = true; + return false; +} + +void hand_item_handler::parse(p_file_handler* pfhndl) { + hnd_itm hnd_itm; + hnd_itm.read(pfhndl->get_data(), pfhndl->get_size()); + if (hnd_itm.ready) + hand_item_load(&data_list[DATA_AFT], hand_items, hnd_itm); +} + +void hand_item_handler::read() { + loaded = false; + + data_struct* aft_data = &data_list[DATA_AFT]; + for (const std::string& i : mdata_manager_get()->GetPrefixes()) { + std::string dir; + dir.assign("rom/"); + dir.append(i); + + const char* file = "hand_item_data.txt"; + + if (!aft_data->check_file_exists(dir.c_str(), file)) + continue; + + p_file_handler* pfhndl = new p_file_handler; + pfhndl->read_file(aft_data, dir.c_str(), file); + file_handlers.push_back(pfhndl); + } +} + +static void hand_item_load(data_struct* data, + std::map, hand_item>& hand_items, hnd_itm& hnd_itm_file) { + object_database* aft_obj_db = &data->data_ft.obj_db; + + for (hnd_itm_data& i : hnd_itm_file.data) { + hand_item itm; + itm.obj_left = aft_obj_db->get_object_info(i.objname_left.c_str()); + itm.obj_right = aft_obj_db->get_object_info(i.objname_right.c_str()); + itm.item_str.assign(i.item_str); + itm.item_name.assign(i.item_name); + itm.file_size = i.file_size; + itm.hand_scale = i.hand_scale; + itm.uid = i.uid; + + uint32_t hand_motion_id = mtp_hand_array_get_mottbl_index(i.hand_motion.c_str()); + if (hand_motion_id == -1) + continue; + + itm.hand_mottbl_index = hand_motion_id; + hand_items.insert({ { itm.uid, CHARA_NONE }, itm }); + } +} + +static int32_t mtp_hand_array_get_mottbl_index(const char* str) { + static const std::pair mtp_hand_array[] = { + { "MTP_HAND_NULL" , 0xC0 }, + { "MTP_HAND_RESET" , 0xC1 }, + { "MTP_HAND_NORMAL" , 0xC2 }, + { "MTP_HAND_OPEN" , 0xC3 }, + { "MTP_HAND_CLOSE" , 0xC4 }, + { "MTP_HAND_PEACE" , 0xC5 }, + { "MTP_HAND_GOOD" , 0xC6 }, + { "MTP_HAND_ONE" , 0xC7 }, + { "MTP_HAND_THREE" , 0xC8 }, + { "MTP_HAND_MEGI" , 0xC9 }, + { "MTP_HAND_SIZEN" , 0xCA }, + { "MTP_HAND_PICK" , 0xCB }, + { "MTP_HAND_MIC" , 0xCC }, + { "MTP_HAND_FAN" , 0xCD }, + { "MTP_HAND_BOTTLE" , 0xCE }, + { "MTP_HAND_PHONE" , 0xCF }, + { "MTP_HAND_HOLD" , 0xD0 }, + { "MTP_HAND_FLASHLIGHT", 0xD1 }, + { "MTP_HAND_MIC_BLK" , 0xD2 }, + { "MTP_HAND_MIC_SLV" , 0xD3 }, + { "MTP_HAND_CUPICE" , 0xD4 }, + { "MTP_HAND_ICEBAR" , 0xD5 }, + }; + + for (auto& i : mtp_hand_array) + if (!str_utils_compare(i.first, str)) + return i.second; + return -1; +} diff --git a/src/CRE/hand_item.hpp b/src/CRE/hand_item.hpp new file mode 100644 index 00000000..02ad6c3c --- /dev/null +++ b/src/CRE/hand_item.hpp @@ -0,0 +1,36 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#pragma once + +#include +#include "../KKdLib/default.hpp" +#include "../KKdLib/database/item_table.hpp" +#include "../KKdLib/database/object.hpp" +#include "../KKdLib/mat.hpp" +#include "../KKdLib/vec.hpp" + +struct hand_item { + object_info obj_left; + object_info obj_right; + std::string item_str; + std::string item_name; + uint32_t file_size; + uint32_t hand_mottbl_index; + float_t hand_scale; + int32_t uid; + + hand_item(); + ~hand_item(); +}; + +extern void hand_item_handler_data_init(); +extern void hand_item_handler_data_clear(); +extern const hand_item* hand_item_handler_data_get_hand_item(int32_t uid, chara_index chara_index); +extern const std::map, hand_item>& hand_item_handler_data_get_hand_items(); +extern int32_t hand_item_handler_data_get_hand_item_uid(const char* str); +extern bool hand_item_handler_data_load(); +extern void hand_item_handler_data_read(); +extern void hand_item_handler_data_free(); diff --git a/src/CRE/item_table.cpp b/src/CRE/item_table.cpp index 9024ee8b..cb1a1dca 100644 --- a/src/CRE/item_table.cpp +++ b/src/CRE/item_table.cpp @@ -3,73 +3,35 @@ GitHub/GitLab: korenkonder */ +#include "item_table.hpp" #include "../KKdLib/io/memory_stream.hpp" #include "../KKdLib/io/path.hpp" #include "../KKdLib/str_utils.hpp" #include "data.hpp" -#include "item_table.hpp" +#include "file_handler.hpp" +#include "mdata_manager.hpp" -static bool item_table_array_read(data_struct* data, const char* path); -static item_table_item* item_table_get_item(item_table* itm_tbl, int32_t item_no); -static void item_table_load(data_struct* data, item_table* itm_tbl, itm_table* itm_tbl_file); +struct item_table_handler { + chara_index chara_index; + std::list file_handlers; + bool loaded; + const char* file; + item_table table; -item_table* item_table_array; + item_table_handler(); + ~item_table_handler(); -void item_table_array_init() { - item_table_array = new item_table[CHARA_MAX]; -} + void clear(); + const item_table_item* get_item(int32_t item_no); + bool load(); + void parse(p_file_handler* pfhndl); + void read(); + void set_path(::chara_index chara_index); +}; -item_cos_data* item_table_array_get_item_cos_data_by_module_index( - chara_index chara_index, int32_t module_index) { - item_table* table = item_table_array_get_table(chara_index); - if (!table) - return 0; +static void item_table_load(data_struct* data, item_table& itm_tbl, itm_table& itm_tbl_file); - auto elem = table->cos.find(module_index); - if (elem != table->cos.end()) - return &elem->second; - else if (table->cos.size()) - return &table->cos.begin()->second; - return 0; -} - -item_table_item* item_table_array_get_item(chara_index chara_index, int32_t item_no) { - if (chara_index < CHARA_MIKU || chara_index >= CHARA_MAX) - return 0; - return item_table_get_item(&item_table_array[chara_index], item_no); -} - -void item_table_array_get_item_objset(chara_index chara_index, - int32_t item_no, std::vector** objset) { - *objset = 0; - item_table_item* item = item_table_array_get_item(chara_index, item_no); - if (item) - *objset = &item->objset; -} - -item_table* item_table_array_get_table(chara_index chara_index) { - if (chara_index < CHARA_MIKU || chara_index >= CHARA_MAX) - return 0; - return &item_table_array[chara_index]; -} - -bool item_table_array_load_file(void* data, const char* path, const char* file, uint32_t hash) { - size_t file_len = utf8_length(file); - - const char* t = strrchr(file, '.'); - if (t) - file_len = t - file; - - std::string s; - s.assign(path); - s.append(file, file_len); - - return item_table_array_read((data_struct*)data, s.c_str()); -} - -void item_table_array_free() { - delete[] item_table_array; -} +item_table_handler* item_table_handler_array; item_table_item_data_obj::item_table_item_data_obj() : rpk() { @@ -121,84 +83,182 @@ item_table::~item_table() { } -static bool item_table_array_read(data_struct* data, const char* path) { - if (!path) - return false; - - char* path_farc = str_utils_add(path, ".farc"); - if (!path_check_file_exists(path_farc)) { - free_def(path_farc); - return false; - } - - farc f; - f.read(path_farc, true, false); - - for (int32_t i = CHARA_MIKU; i < CHARA_MAX; i++) { - const char* file = ""; - switch (i) { - case CHARA_MIKU: - file = "mikitm_tbl.txt"; - break; - case CHARA_RIN: - file = "rinitm_tbl.txt"; - break; - case CHARA_LEN: - file = "lenitm_tbl.txt"; - break; - case CHARA_LUKA: - file = "lukitm_tbl.txt"; - break; - case CHARA_NERU: - file = "neritm_tbl.txt"; - break; - case CHARA_HAKU: - file = "hakitm_tbl.txt"; - break; - case CHARA_KAITO: - file = "kaiitm_tbl.txt"; - break; - case CHARA_MEIKO: - file = "meiitm_tbl.txt"; - break; - case CHARA_SAKINE: - file = "sakitm_tbl.txt"; - break; - case CHARA_TETO: - file = "tetitm_tbl.txt"; - break; - } - - farc_file* ff = f.read_file(file); - if (ff) { - memory_stream s; - s.open(ff->data, ff->size); - itm_table itm; - itm.read(ff->data, ff->size); - if (itm.ready) - item_table_load(data, &item_table_array[i], &itm); - } - } - free_def(path_farc); - return true; -} - -static item_table_item* item_table_get_item(item_table* itm_tbl_hndl, int32_t item_no) { - auto elem = itm_tbl_hndl->item.find(item_no); - if (elem != itm_tbl_hndl->item.end() && elem->second.type != -1) +const item_table_item* item_table::get_item(int32_t item_no) { + auto elem = item.find(item_no); + if (elem != item.end() && elem->second.type != -1) return &elem->second; return 0; } -static void item_table_load(data_struct* data, item_table* itm_tbl, itm_table* itm_tbl_file) { - for (itm_table_item& i : itm_tbl_file->item) { +void item_table_handler_array_init() { + item_table_handler_array = new item_table_handler[CHARA_MAX]; + for (int32_t i = CHARA_MIKU; i < CHARA_MAX; i++) + item_table_handler_array[i].set_path((chara_index)i); +} + +const item_cos_data* item_table_handler_array_get_item_cos_data_by_module_index( + chara_index chara_index, int32_t module_index) { + const item_table* table = item_table_handler_array_get_table(chara_index); + if (!table) + return 0; + + auto elem = table->cos.find(module_index); + if (elem != table->cos.end()) + return &elem->second; + else if (table->cos.size()) + return &table->cos.begin()->second; + return 0; +} + +const item_table_item* item_table_handler_array_get_item( + chara_index chara_index, int32_t item_no) { + if (chara_index >= CHARA_MIKU && chara_index < CHARA_MAX) + return item_table_handler_array[chara_index].get_item(item_no); + return 0; +} + +const std::vector* item_table_handler_array_get_item_objset( + chara_index chara_index, int32_t item_no) { + const item_table_item* item = item_table_handler_array_get_item(chara_index, item_no); + if (item) + return &item->objset; + return 0; +} + +const item_table* item_table_handler_array_get_table(chara_index chara_index) { + if (chara_index < CHARA_MIKU || chara_index >= CHARA_MAX) + return 0; + return &item_table_handler_array[chara_index].table; +} + +bool item_table_handler_array_load() { + bool ret = false; + for (int32_t i = CHARA_MIKU; i < CHARA_MAX; i++) + ret |= item_table_handler_array[i].load(); + return ret; +} + +void item_table_handler_array_read() { + for (int32_t i = CHARA_MIKU; i < CHARA_MAX; i++) + item_table_handler_array[i].read(); +} + +void item_table_handler_array_free() { + delete[] item_table_handler_array; +} + +item_table_handler::item_table_handler() : file(), loaded() { + chara_index = CHARA_NONE; +} + +item_table_handler::~item_table_handler() { + +} + +void item_table_handler::clear() { + for (p_file_handler*& i : file_handlers) + if (i) { + delete i; + i = 0; + } + file_handlers.clear(); + loaded = false; +} + +const item_table_item* item_table_handler::get_item(int32_t item_no) { + return table.get_item(item_no); +} + +bool item_table_handler::load() { + if (chara_index < CHARA_MIKU || chara_index >= CHARA_MAX || loaded || !file) + return false; + + for (p_file_handler*& i : file_handlers) + if (i->check_not_ready()) + return true; + + for (p_file_handler*& i : file_handlers) { + if (!i) + continue; + + parse(i); + + delete i; + i = 0; + } + file_handlers.clear(); + + loaded = true; + return false; +} + +void item_table_handler::parse(p_file_handler* pfhndl) { + itm_table itm; + itm.read(pfhndl->get_data(), pfhndl->get_size()); + if (itm.ready) + item_table_load(&data_list[DATA_AFT], table, itm); +} + +void item_table_handler::read() { + if (!file) + return; + + for (p_file_handler*& i : file_handlers) + if (i) { + delete i; + i = 0; + } + file_handlers.clear(); + + loaded = false; + + data_struct* aft_data = &data_list[DATA_AFT]; + for (const std::string& i : mdata_manager_get()->GetPrefixes()) { + std::string dir; + dir.assign("rom/"); + dir.append(i); + + const char* farc_file = "chritm_prop.farc"; + + if (!aft_data->check_file_exists(dir.c_str(), farc_file)) + continue; + + p_file_handler* pfhndl = new p_file_handler; + pfhndl->read_file(aft_data, dir.c_str(), farc_file, file, false); + file_handlers.push_back(pfhndl); + } +} + +void item_table_handler::set_path(::chara_index chara_index) { + const char* item_table_paths[] = { + "mikitm_tbl.txt", + "rinitm_tbl.txt", + "lenitm_tbl.txt", + "lukitm_tbl.txt", + "neritm_tbl.txt", + "hakitm_tbl.txt", + "kaiitm_tbl.txt", + "meiitm_tbl.txt", + "sakitm_tbl.txt", + "tetitm_tbl.txt", + }; + + this->chara_index = chara_index; + this->file = item_table_paths[chara_index]; +} + +static void item_table_load(data_struct* data, item_table& itm_tbl, itm_table& itm_tbl_file) { + object_database* aft_obj_db = &data->data_ft.obj_db; + texture_database* aft_tex_db = &data->data_ft.tex_db; + + for (itm_table_item& i : itm_tbl_file.item) { item_table_item itm; itm.flag = i.flag; itm.name = i.name; itm.objset.reserve(i.objset.size()); for (std::string& j : i.objset) { - uint32_t set_id = data->data_ft.obj_db.get_object_set_id(j.c_str()); + uint32_t set_id = aft_obj_db->get_object_set_id(j.c_str()); if (set_id != -1) itm.objset.push_back(set_id); } @@ -209,7 +269,7 @@ static void item_table_load(data_struct* data, item_table* itm_tbl, itm_table* i itm.sub_id = (item_sub_id)i.sub_id; for (itm_table_item_data_obj& j : i.data.obj) { - object_info obj_info = data->data_ft.obj_db.get_object_info(j.uid.c_str()); + object_info obj_info = aft_obj_db->get_object_info(j.uid.c_str()); if (obj_info.is_null()) continue; @@ -230,8 +290,8 @@ static void item_table_load(data_struct* data, item_table* itm_tbl, itm_table* i } for (itm_table_item_data_tex& j : i.data.tex) { - uint32_t org = data->data_ft.tex_db.get_texture_id(j.org.c_str()); - uint32_t chg = data->data_ft.tex_db.get_texture_id(j.chg.c_str()); + uint32_t org = aft_tex_db->get_texture_id(j.org.c_str()); + uint32_t chg = aft_tex_db->get_texture_id(j.chg.c_str()); if (org == -1 || chg == -1) continue; @@ -242,7 +302,7 @@ static void item_table_load(data_struct* data, item_table* itm_tbl, itm_table* i } for (itm_table_item_data_col& j : i.data.col) { - uint32_t tex_id = data->data_ft.tex_db.get_texture_id(j.tex.c_str()); + uint32_t tex_id = aft_tex_db->get_texture_id(j.tex.c_str()); if (tex_id == -1) continue; @@ -258,32 +318,32 @@ static void item_table_load(data_struct* data, item_table* itm_tbl, itm_table* i itm.org_itm = i.org_itm; itm.npr_flag = i.npr_flag; itm.face_depth = i.face_depth; - itm_tbl->item.insert_or_assign(i.no, itm); + itm_tbl.item.insert_or_assign(i.no, itm); } - for (itm_table_dbgset& i : itm_tbl_file->dbgset) { - uint32_t set_id = data->data_ft.obj_db.get_object_set_id(i.name.c_str()); + for (itm_table_dbgset& i : itm_tbl_file.dbgset) { + uint32_t set_id = aft_obj_db->get_object_set_id(i.name.c_str()); if (set_id == -1) continue; item_table_dbgset dbg; dbg.item = i.item; - itm_tbl->dbgset.insert_or_assign(set_id, dbg); + itm_tbl.dbgset.insert_or_assign(set_id, dbg); } - for (itm_table_cos& i : itm_tbl_file->cos) { + for (itm_table_cos& i : itm_tbl_file.cos) { if (!i.item.size()) continue; item_cos_data cos = {}; for (int32_t j : i.item) { - item_table_item* item = item_table_get_item(itm_tbl, j); + const item_table_item* item = itm_tbl.get_item(j); if (item) cos.arr[item->sub_id] = j; } - itm_tbl->cos.insert_or_assign(i.id, cos); + itm_tbl.cos.insert_or_assign(i.id, cos); } item_cos_data cos = {}; - itm_tbl->cos.insert_or_assign(499, cos); + itm_tbl.cos.insert_or_assign(499, cos); } diff --git a/src/CRE/item_table.hpp b/src/CRE/item_table.hpp index 66cbb393..6b0e7c21 100644 --- a/src/CRE/item_table.hpp +++ b/src/CRE/item_table.hpp @@ -117,14 +117,18 @@ struct item_table { item_table(); ~item_table(); + + const item_table_item* get_item(int32_t item_no); }; -extern void item_table_array_init(); -extern item_cos_data* item_table_array_get_item_cos_data_by_module_index( +extern void item_table_handler_array_init(); +extern const item_cos_data* item_table_handler_array_get_item_cos_data_by_module_index( chara_index chara_index, int32_t module_index); -extern item_table_item* item_table_array_get_item(chara_index chara_index, int32_t item_no); -extern void item_table_array_get_item_objset(chara_index chara_index, - int32_t item_no, std::vector** objset); -extern item_table* item_table_array_get_table(chara_index chara_index); -extern bool item_table_array_load_file(void* data, const char* path, const char* file, uint32_t hash); -extern void item_table_array_free(); +extern const item_table_item* item_table_handler_array_get_item( + chara_index chara_index, int32_t item_no); +extern const std::vector* item_table_handler_array_get_item_objset( + chara_index chara_index, int32_t item_no); +extern const item_table* item_table_handler_array_get_table(chara_index chara_index); +extern bool item_table_handler_array_load(); +extern void item_table_handler_array_read(); +extern void item_table_handler_array_free(); diff --git a/src/CRE/light_param/face.cpp b/src/CRE/light_param/face.cpp index 248ef5c5..4501535d 100644 --- a/src/CRE/light_param/face.cpp +++ b/src/CRE/light_param/face.cpp @@ -12,7 +12,7 @@ face::face() { direction = 0.0f; } -float_t face::get_offset() { +float_t face::get_offset() const { return offset; } @@ -20,7 +20,7 @@ void face::set_offset(float_t value) { offset = value; } -float_t face::get_scale() { +float_t face::get_scale() const { return scale; } @@ -28,26 +28,26 @@ void face::set_scale(float_t value) { scale = value; } -void face::get_position(vec3& value) { +void face::get_position(vec3& value) const { value = position; } -void face::set_position(vec3& value) { +void face::set_position(const vec3& value) { position = value; } -void face::set_position(vec3&& value) { +void face::set_position(const vec3&& value) { position = value; } -void face::get_direction(vec3& value) { +void face::get_direction(vec3& value) const { value = direction; } -void face::set_direction(vec3& value) { +void face::set_direction(const vec3& value) { direction = value; } -void face::set_direction(vec3&& value) { +void face::set_direction(const vec3&& value) { direction = value; } diff --git a/src/CRE/light_param/face.hpp b/src/CRE/light_param/face.hpp index 88a84df7..f7ee8c2c 100644 --- a/src/CRE/light_param/face.hpp +++ b/src/CRE/light_param/face.hpp @@ -18,14 +18,14 @@ struct face { face(); - float_t get_offset(); + float_t get_offset() const; void set_offset(float_t value); - float_t get_scale(); + float_t get_scale() const; void set_scale(float_t value); - void get_position(vec3& value); - void set_position(vec3& value); - void set_position(vec3&& value); - void get_direction(vec3& value); - void set_direction(vec3& value); - void set_direction(vec3&& value); + void get_position(vec3& value) const; + void set_position(const vec3& value); + void set_position(const vec3&& value); + void get_direction(vec3& value) const; + void set_direction(const vec3& value); + void set_direction(const vec3&& value); }; diff --git a/src/CRE/light_param/fog.cpp b/src/CRE/light_param/fog.cpp index fc9e04f3..8b7d142c 100644 --- a/src/CRE/light_param/fog.cpp +++ b/src/CRE/light_param/fog.cpp @@ -5,6 +5,7 @@ #include "fog.hpp" #include "../shader_ft.hpp" +#include "../render_context.hpp" fog::fog() { type = FOG_NONE; @@ -15,7 +16,7 @@ fog::fog() { color = { 1.0f, 1.0f, 1.0f, 0.0f }; } -fog_type fog::get_type() { +fog_type fog::get_type() const { return type; } @@ -23,7 +24,7 @@ void fog::set_type(fog_type value) { type = value; } -float_t fog::get_density() { +float_t fog::get_density() const { return density; } @@ -31,7 +32,7 @@ void fog::set_density(float_t value) { density = value; } -float_t fog::get_start() { +float_t fog::get_start() const { return start; } @@ -39,7 +40,7 @@ void fog::set_start(float_t value) { start = value; } -float_t fog::get_end() { +float_t fog::get_end() const { return end; } @@ -47,7 +48,7 @@ void fog::set_end(float_t value) { end = value; } -int32_t fog::get_index() { +int32_t fog::get_index() const { return index; } @@ -55,15 +56,15 @@ void fog::set_index(int32_t value) { index = value; } -void fog::get_color(vec4& value) { +void fog::get_color(vec4& value) const { value = color; } -void fog::set_color(vec4& value) { +void fog::set_color(const vec4& value) { color = value; } -void fog::set_color(vec4&& value) { +void fog::set_color(const vec4&& value) { color = value; } @@ -84,25 +85,19 @@ void fog::data_set(fog_id id) { params.z = end; params.w = 1.0f / (end - start); - switch (id) { - case FOG_DEPTH: { - vec4 color; - get_color(color); - shaders_ft.env_vert_set(29, color); - shaders_ft.env_frag_set(4, color); + extern render_context* rctx_ptr; - shaders_ft.state_fog_set_color(color * (float_t)(1.0 / 8.0)); - shaders_ft.state_fog_set_params(params); - } break; - case FOG_HEIGHT: { - vec4 color; - get_color(color); - shaders_ft.env_vert_set(30, color); - shaders_ft.env_frag_set(11, color); - shaders_ft.env_vert_set(14, params); - } break; - case FOG_BUMP: { - shaders_ft.env_vert_set(19, params); - } break; + switch (id) { + case FOG_DEPTH: + get_color(rctx_ptr->obj_scene.g_fog_depth_color); + rctx_ptr->obj_scene.g_fog_state_params = params; + break; + case FOG_HEIGHT: + get_color(rctx_ptr->obj_scene.g_fog_height_color); + rctx_ptr->obj_scene.g_fog_height_params = params; + break; + case FOG_BUMP: + rctx_ptr->obj_scene.g_fog_bump_params = params; + break; } } diff --git a/src/CRE/light_param/fog.hpp b/src/CRE/light_param/fog.hpp index 0accb065..684b8d59 100644 --- a/src/CRE/light_param/fog.hpp +++ b/src/CRE/light_param/fog.hpp @@ -20,18 +20,18 @@ struct fog { fog(); - fog_type get_type(); + fog_type get_type() const; void set_type(fog_type value); - float_t get_density(); + float_t get_density() const; void set_density(float_t value); - float_t get_start(); + float_t get_start() const; void set_start(float_t value); - float_t get_end(); + float_t get_end() const; void set_end(float_t value); - int32_t get_index(); + int32_t get_index() const; void set_index(int32_t value); - void get_color(vec4& value); - void set_color(vec4& value); - void set_color(vec4&& value); + void get_color(vec4& value) const; + void set_color(const vec4& value); + void set_color(const vec4&& value); void data_set(fog_id id); }; diff --git a/src/CRE/light_param/light.cpp b/src/CRE/light_param/light.cpp index 67392754..d62c1463 100644 --- a/src/CRE/light_param/light.cpp +++ b/src/CRE/light_param/light.cpp @@ -5,16 +5,16 @@ #include "light.hpp" #include "../../KKdLib/mat.hpp" -#include "../shader_ft.hpp" +#include "../render_context.hpp" extern bool light_chara_ambient; -extern vec4 npr_spec_color; +extern vec4 npr_cloth_spec_color; -static void light_get_direction_from_position(vec4* pos_dir, light_data* light, bool force); +static void light_get_direction_from_position(vec4* pos_dir, light_data* light, bool force = false); light_data::light_data() : type(), ambient(), diffuse(), specular(), position(), spot_direction(), spot_exponent(), spot_cutoff(), attenuation(), -ibl_specular(), ibl_back(), ibl_direction(), tone_curve(), clip_plane() { +ibl_color0(), ibl_color1(), ibl_direction(), tone_curve(), clip_plane() { set_type(LIGHT_OFF); set_ambient({ 0.0f, 0.0, 0.0f, 1.0f }); set_diffuse({ 1.0f, 1.0, 1.0f, 0.0f }); @@ -26,8 +26,8 @@ ibl_specular(), ibl_back(), ibl_direction(), tone_curve(), clip_plane() { set_constant(1.0f); set_linear(0.0f); set_quadratic(0.0f); - set_ibl_specular({ 0.0f, 0.0f, 0.0f, 0.0f }); - set_ibl_back({ 0.0f, 0.0f, 0.0f, 0.0f }); + set_ibl_color0({ 0.0f, 0.0f, 0.0f, 0.0f }); + set_ibl_color1({ 0.0f, 0.0f, 0.0f, 0.0f }); set_ibl_direction({ 0.0f, 0.0f, 0.0f, 0.0f }); set_tone_curve({ 0.0f, 0.0f, 0.0f }); } @@ -40,7 +40,7 @@ irradiance_r(), irradiance_g(), irradiance_b() { set_irradiance(mat4_identity, mat4_identity, mat4_identity); } -light_type light_data::get_type() { +light_type light_data::get_type() const { return type; } @@ -52,7 +52,7 @@ void light_data::set_type(light_type value) { position.w = 1.0f; } -void light_data::get_ambient(vec4& value) { +void light_data::get_ambient(vec4& value) const { value = ambient; } @@ -86,7 +86,7 @@ void light_data::set_ambient(const vec4&& value, bool set[4]) { ambient.w = value.w; } -void light_data::get_diffuse(vec4& value) { +void light_data::get_diffuse(vec4& value) const { value = diffuse; } @@ -120,7 +120,7 @@ void light_data::set_diffuse(const vec4&& value, bool set[4]) { diffuse.w = value.w; } -void light_data::get_specular(vec4& value) { +void light_data::get_specular(vec4& value) const { value = specular; } @@ -154,7 +154,7 @@ void light_data::set_specular(const vec4&& value, bool set[4]) { specular.w = value.w; } -void light_data::get_position(vec3& value) { +void light_data::get_position(vec3& value) const { value = *(vec3*)&position; } @@ -166,7 +166,7 @@ void light_data::set_position(const vec3&& value) { *(vec3*)&position = value; } -void light_data::get_position(vec4& value) { +void light_data::get_position(vec4& value) const { value = position; } @@ -178,7 +178,7 @@ void light_data::set_position(const vec4&& value) { position = value; } -void light_data::get_spot_direction(vec3& value) { +void light_data::get_spot_direction(vec3& value) const { value = spot_direction; } @@ -190,7 +190,7 @@ void light_data::set_spot_direction(const vec3&& value) { spot_direction = value; } -float_t light_data::get_spot_exponent() { +float_t light_data::get_spot_exponent() const { return spot_exponent; } @@ -198,7 +198,7 @@ void light_data::set_spot_exponent(float_t value) { spot_exponent = value; } -float_t light_data::get_spot_cutoff() { +float_t light_data::get_spot_cutoff() const { return spot_cutoff; } @@ -206,7 +206,7 @@ void light_data::set_spot_cutoff(float_t value) { spot_cutoff = value; } -float_t light_data::get_constant() { +float_t light_data::get_constant() const { return attenuation.constant; } @@ -214,7 +214,7 @@ void light_data::set_constant(float_t value) { attenuation.constant = value; } -float_t light_data::get_linear() { +float_t light_data::get_linear() const { return attenuation.linear; } @@ -222,7 +222,7 @@ void light_data::set_linear(float_t value) { attenuation.linear = value; } -float_t light_data::get_quadratic() { +float_t light_data::get_quadratic() const { return attenuation.quadratic; } @@ -230,7 +230,7 @@ void light_data::set_quadratic(float_t value) { attenuation.quadratic = value; } -void light_data::get_attenuation(light_attenuation& value) { +void light_data::get_attenuation(light_attenuation& value) const { value = attenuation; } @@ -242,31 +242,31 @@ void light_data::set_attenuation(const light_attenuation&& value) { attenuation = value; } -void light_data::get_ibl_specular(vec4& value) { - value = ibl_specular; +void light_data::get_ibl_color0(vec4& value) const { + value = ibl_color0; } -void light_data::set_ibl_specular(const vec4& value) { - ibl_specular = value; +void light_data::set_ibl_color0(const vec4& value) { + ibl_color0 = value; } -void light_data::set_ibl_specular(const vec4&& value) { - ibl_specular = value; +void light_data::set_ibl_color0(const vec4&& value) { + ibl_color0 = value; } -void light_data::get_ibl_back(vec4& value) { - value = ibl_back; +void light_data::get_ibl_color1(vec4& value) const { + value = ibl_color1; } -void light_data::set_ibl_back(const vec4& value) { - ibl_back = value; +void light_data::set_ibl_color1(const vec4& value) { + ibl_color1 = value; } -void light_data::set_ibl_back(const vec4&& value) { - ibl_back = value; +void light_data::set_ibl_color1(const vec4&& value) { + ibl_color1 = value; } -void light_data::get_ibl_direction(vec4& value) { +void light_data::get_ibl_direction(vec4& value) const { value = ibl_direction; } @@ -278,7 +278,7 @@ void light_data::set_ibl_direction(const vec4&& value) { ibl_direction = value; } -void light_data::get_tone_curve(light_tone_curve& value) { +void light_data::get_tone_curve(light_tone_curve& value) const { value = tone_curve; } @@ -290,7 +290,7 @@ void light_data::set_tone_curve(const light_tone_curve&& value) { tone_curve = value; } -void light_data::get_clip_plane(light_clip_plane& value) { +void light_data::get_clip_plane(light_clip_plane& value) const { value = clip_plane; } @@ -333,228 +333,189 @@ void light_set::set_irradiance(const mat4&& r, const mat4&& g, const mat4&& b) { } void light_set::data_set(face& face, light_set_id id) { - vec4 ambient_intensity; - get_ambient_intensity(ambient_intensity); - shaders_ft.state_lightmodel_set_ambient(false, ambient_intensity); + static const float_t spec_coef = (float_t)(1.0 / (1.0 - cos(18.0 * DEG_TO_RAD))); + static const float_t luce_coef = (float_t)(1.0 / (1.0 - cos(45.0 * DEG_TO_RAD))); - for (int32_t i = LIGHT_CHARA; i <= LIGHT_PROJECTION; i++) { - light_data* light = &lights[i]; - if (light_chara_ambient || i != LIGHT_CHARA) { - vec4 ambient = light->ambient; - shaders_ft.state_light_set_ambient(i, ambient); - } - else - shaders_ft.state_light_set_ambient(i, 0.0f, 0.0f, 0.0f, 1.0f); + extern render_context* rctx_ptr; + obj_scene_shader_data& obj_scene = rctx_ptr->obj_scene; - vec4 diffuse = light->diffuse; - vec4 specular = light->specular; - shaders_ft.state_light_set_diffuse(i, diffuse); - shaders_ft.state_light_set_specular(i, specular); + lights[LIGHT_STAGE].get_diffuse(obj_scene.g_light_env_stage_diffuse); + lights[LIGHT_STAGE].get_ambient(obj_scene.g_light_env_stage_specular); - vec4 light_position = light->position; - if (light->type == LIGHT_PARALLEL) - light_get_direction_from_position(&light_position, 0, true); - shaders_ft.state_light_set_position(i, light_position); + lights[LIGHT_CHARA].get_diffuse(obj_scene.g_light_env_chara_diffuse); + lights[LIGHT_CHARA].get_ambient(obj_scene.g_light_env_chara_ambient); + lights[LIGHT_CHARA].get_specular(obj_scene.g_light_env_chara_specular); + + lights[LIGHT_REFLECT].get_diffuse(obj_scene.g_light_env_reflect_diffuse); + lights[LIGHT_REFLECT].get_ambient(obj_scene.g_light_env_reflect_ambient); - light_attenuation attenuation = light->attenuation; - shaders_ft.state_light_set_attenuation(i, - attenuation.constant, attenuation.linear, attenuation.quadratic, light->spot_exponent); + lights[LIGHT_PROJECTION].get_diffuse(obj_scene.g_light_env_proj_diffuse); + lights[LIGHT_PROJECTION].get_specular(obj_scene.g_light_env_proj_specular); + lights[LIGHT_PROJECTION].get_position(obj_scene.g_light_env_proj_position); + if (lights[LIGHT_PROJECTION].get_type() == LIGHT_PARALLEL) + light_get_direction_from_position(&obj_scene.g_light_env_proj_position, 0, true); - vec3 spot_direction = light->spot_direction; - shaders_ft.state_light_set_spot_direction(i, - spot_direction.x, spot_direction.y, spot_direction.z, light->spot_cutoff); - } + vec4 light_chara_dir; + vec4 light_chara_ibl_color0; + vec4 light_chara_ibl_color1; + vec4 light_chara_specular; + lights[LIGHT_CHARA].get_position(light_chara_dir); + light_get_direction_from_position(&light_chara_dir, &lights[LIGHT_CHARA]); + lights[LIGHT_CHARA].get_ibl_color0(light_chara_ibl_color0); + lights[LIGHT_CHARA].get_ibl_color1(light_chara_ibl_color1); + lights[LIGHT_CHARA].get_specular(light_chara_specular); - const float_t spec_coef = (float_t)(1.0 / (1.0 - cos(18.0 * DEG_TO_RAD))); - const float_t luce_coef = (float_t)(1.0 / (1.0 - cos(45.0 * DEG_TO_RAD))); + obj_scene.g_light_chara_dir = light_chara_dir; + obj_scene.g_light_chara_spec = light_chara_specular * light_chara_ibl_color0 * spec_coef; + obj_scene.g_light_chara_luce = light_chara_ibl_color0 * luce_coef; + obj_scene.g_light_chara_back = light_chara_specular * light_chara_ibl_color1 * spec_coef; - vec4 ambient; - vec4 diffuse; - vec4 specular; - vec4 position; - vec4 ibl_specular; - vec4 ibl_back; - vec4 ibl_direction; - vec4 temp; - lights[LIGHT_CHARA].get_position(position); - light_get_direction_from_position(&position, &lights[LIGHT_CHARA], false); - lights[LIGHT_CHARA].get_ibl_specular(ibl_specular); - lights[LIGHT_CHARA].get_ibl_back(ibl_back); - lights[LIGHT_CHARA].get_diffuse(diffuse); - lights[LIGHT_CHARA].get_specular(specular); + vec4 light_stage_dir; + vec4 light_stage_ibl_color; + vec4 light_stage_diffuse; + vec4 light_stage_specular; + lights[LIGHT_STAGE].get_position(light_stage_dir); + light_get_direction_from_position(&light_stage_dir, &lights[LIGHT_STAGE]); + lights[LIGHT_STAGE].get_ibl_color0(light_stage_ibl_color); + lights[LIGHT_STAGE].get_diffuse(light_stage_diffuse); + lights[LIGHT_STAGE].get_specular(light_stage_specular); - shaders_ft.env_vert_set(5, position); - shaders_ft.env_frag_set(5, position); + obj_scene.g_light_stage_dir = light_stage_dir; + obj_scene.g_light_stage_diff = light_stage_diffuse * light_stage_ibl_color; + obj_scene.g_light_stage_spec = light_stage_specular * light_stage_ibl_color * spec_coef; - temp = diffuse * ibl_specular; - shaders_ft.env_vert_set(6, temp); - shaders_ft.env_frag_set(6, temp); - - temp = specular * ibl_specular * spec_coef; - shaders_ft.env_vert_set(7, temp); - shaders_ft.env_frag_set(7, temp); - - temp = ibl_specular * luce_coef; - shaders_ft.env_vert_set(8, temp); - shaders_ft.env_frag_set(8, temp); - - temp = specular * ibl_back * spec_coef; - shaders_ft.env_vert_set(9, temp); - shaders_ft.env_frag_set(9, temp); - - lights[LIGHT_STAGE].get_position(position); - light_get_direction_from_position(&position, &lights[LIGHT_STAGE], false); - lights[LIGHT_STAGE].get_ibl_specular(ibl_specular); - lights[LIGHT_STAGE].get_diffuse(diffuse); - lights[LIGHT_STAGE].get_specular(specular); - - shaders_ft.env_vert_set(10, position); - shaders_ft.env_frag_set(14, position); - - temp = diffuse * ibl_specular; - shaders_ft.env_vert_set(11, temp); - shaders_ft.env_frag_set(16, temp); - - temp = specular * ibl_specular * spec_coef; - shaders_ft.env_vert_set(12, temp); - shaders_ft.env_frag_set(17, temp); - - mat4 irradiance_r; - mat4 irradiance_g; - mat4 irradiance_b; - get_irradiance(irradiance_r, irradiance_g, irradiance_b); - shaders_ft.state_matrix_set_program(0, irradiance_r); - shaders_ft.state_matrix_set_program(1, irradiance_g); - shaders_ft.state_matrix_set_program(2, irradiance_b); + mat4 irradiance_r_transforms; + mat4 irradiance_g_transforms; + mat4 irradiance_b_transforms; + get_irradiance(irradiance_r_transforms, irradiance_g_transforms, irradiance_b_transforms); + obj_scene.set_g_irradiance_r_transforms(irradiance_r_transforms); + obj_scene.set_g_irradiance_g_transforms(irradiance_g_transforms); + obj_scene.set_g_irradiance_b_transforms(irradiance_b_transforms); float_t offset = face.get_offset(); float_t v28 = (float_t)(1.0 - exp(offset * -0.44999999)) * 2.0f; offset = max_def(offset, 0.0f); - shaders_ft.env_vert_set(0x11, v28 * 0.1f, offset * 0.06f, 1.0, 1.0); + obj_scene.g_light_face_diff = { v28 * 0.1f, offset * 0.06f, 1.0f, 1.0f }; if (lights[LIGHT_CHARA_COLOR].get_type() == LIGHT_PARALLEL) { - lights[LIGHT_CHARA_COLOR].get_ambient(ambient); - lights[LIGHT_CHARA_COLOR].get_diffuse(diffuse); - lights[LIGHT_CHARA_COLOR].get_specular(specular); - if (specular.w > 1.0) - specular.w = 1.0; - shaders_ft.env_frag_set(33, ambient); - shaders_ft.env_frag_set(34, diffuse); - shaders_ft.env_frag_set(35, specular); + lights[LIGHT_CHARA_COLOR].get_ambient(obj_scene.g_chara_color_rim); + lights[LIGHT_CHARA_COLOR].get_diffuse(obj_scene.g_chara_color0); + lights[LIGHT_CHARA_COLOR].get_specular(obj_scene.g_chara_color1); + if (obj_scene.g_chara_color1.w > 1.0f) + obj_scene.g_chara_color1.w = 1.0f; } else { - shaders_ft.env_frag_set(33, 0.0f); - shaders_ft.env_frag_set(34, 0.0f); - shaders_ft.env_frag_set(35, 0.0f); + obj_scene.g_chara_color_rim = 0.0f; + obj_scene.g_chara_color0 = 0.0f; + obj_scene.g_chara_color1 = 0.0f; } if (lights[LIGHT_TONE_CURVE].get_type() == LIGHT_PARALLEL) { uniform_value[U_TONE_CURVE] = 1; - lights[LIGHT_TONE_CURVE].get_position(position); - light_get_direction_from_position(&position, &lights[LIGHT_TONE_CURVE], false); - lights[LIGHT_TONE_CURVE].get_ambient(ambient); - lights[LIGHT_TONE_CURVE].get_diffuse(diffuse); - lights[LIGHT_TONE_CURVE].get_specular(specular); + vec4 chara_f_dir; + vec4 chara_f_ambient; + vec4 chara_f_diffuse; + vec4 chara_tc_param; + lights[LIGHT_TONE_CURVE].get_position(chara_f_dir); + light_get_direction_from_position(&chara_f_dir, &lights[LIGHT_TONE_CURVE]); + lights[LIGHT_TONE_CURVE].get_ambient(chara_f_ambient); + lights[LIGHT_TONE_CURVE].get_diffuse(chara_f_diffuse); light_tone_curve tone_curve; lights[LIGHT_TONE_CURVE].get_tone_curve(tone_curve); - tone_curve.end_point = min_def(tone_curve.end_point, 1.0f) - tone_curve.start_point; - if (tone_curve.end_point > 0.0f) - tone_curve.end_point = 1.0f / tone_curve.end_point; + chara_tc_param.x = tone_curve.start_point; + float_t end_point = min_def(tone_curve.end_point, 1.0f) - tone_curve.start_point; + if (end_point > 0.0f) + chara_tc_param.y = 1.0f / end_point; else - tone_curve.end_point = 0.0f; + chara_tc_param.y = 0.0f; + chara_tc_param.z = tone_curve.coefficient; + chara_tc_param.w = 0.0f; - shaders_ft.env_frag_set(36, position); - shaders_ft.env_frag_set(37, ambient); - shaders_ft.env_frag_set(38, diffuse); - shaders_ft.env_frag_set(39, specular); - shaders_ft.env_frag_set(40, tone_curve.start_point, - tone_curve.end_point, tone_curve.coefficient, 0.0f); + obj_scene.g_chara_f_dir = chara_f_dir; + obj_scene.g_chara_f_ambient = chara_f_ambient; + obj_scene.g_chara_f_diffuse = chara_f_diffuse; + obj_scene.g_chara_tc_param = chara_tc_param; } else { uniform_value[U_TONE_CURVE] = 0; - shaders_ft.env_frag_set(36, 0.0f, 1.0f, 0.0f, 0.0f); - shaders_ft.env_frag_set(37, 0.0f, 0.0f, 0.0f, 0.0f); - shaders_ft.env_frag_set(38, 0.0f, 0.0f, 0.0f, 0.0f); - shaders_ft.env_frag_set(39, 0.0f, 0.0f, 0.0f, 0.0f); - shaders_ft.env_frag_set(40, 0.0f, 0.0f, 0.0f, 0.0f); + obj_scene.g_chara_f_dir = { 0.0f, 1.0f, 0.0f, 0.0f }; + obj_scene.g_chara_f_ambient = 0.0f; + obj_scene.g_chara_f_diffuse = 0.0f; + obj_scene.g_chara_tc_param = 0.0f; } - lights[LIGHT_CHARA].get_position(position); - if (fabs(position.x) <= 0.000001f && fabs(position.z) <= 0.000001f && fabs(position.z) <= 0.000001f) - position = { 0.0f, 1.0f, 0.0f, 1.0f }; + vec4 light_reflect_dir; + lights[LIGHT_CHARA].get_position(light_reflect_dir); + if (fabs(light_reflect_dir.x) <= 0.000001f && fabs(light_reflect_dir.y) <= 0.000001f + && fabs(light_reflect_dir.z) <= 0.000001f) + *(vec3*)&light_reflect_dir = vec3(0.0f, 1.0f, 0.0f); else { - float_t length = vec3::length(*(vec3*)&position); + float_t length = vec3::length(*(vec3*)&light_reflect_dir); if (length != 0.0f) - *(vec3*)&position = *(vec3*)&position * (1.0f / length); + *(vec3*)&light_reflect_dir *= 1.0f / length; } + light_reflect_dir.w = 1.0f; - shaders_ft.env_vert_set(15, position.x, position.y, position.z, 1.0f); - shaders_ft.env_vert_set(16, -position.x, -position.y, -position.z, 1.0f); + obj_scene.g_light_reflect_dir = light_reflect_dir; if (lights[LIGHT_REFLECT].get_type() == LIGHT_SPOT) { - vec4 spot_direction; - lights[LIGHT_REFLECT].get_spot_direction(*(vec3*)&spot_direction); - *(vec3*)&spot_direction = -*(vec3*)&spot_direction; - light_get_direction_from_position(&spot_direction, &lights[LIGHT_REFLECT], true); + vec3 spot_direction; + vec4 position; + lights[LIGHT_REFLECT].get_spot_direction(spot_direction); lights[LIGHT_REFLECT].get_position(position); - spot_direction.w = -vec3::dot(*(vec3*)&position, *(vec3*)&spot_direction); + vec4 clip_plane; + *(vec3*)&clip_plane = -spot_direction; + light_get_direction_from_position(&clip_plane, &lights[LIGHT_REFLECT], true); + clip_plane.w = -vec3::dot(*(vec3*)&position, *(vec3*)&clip_plane); - shaders_ft.env_vert_set(28, spot_direction); - shaders_ft.env_frag_set(22, spot_direction); - } - else if (lights[LIGHT_REFLECT].get_type() == LIGHT_POINT) { - shaders_ft.env_vert_set(28, 0.0f, -1.0f, 0.0f, 9999.0f); - shaders_ft.env_frag_set(22, 0.0f, -1.0f, 0.0f, 9999.0f); - } - else { - shaders_ft.env_vert_set(28, 0.0f, -1.0f, 0.0f, 0.0f); - shaders_ft.env_frag_set(22, 0.0f, -1.0f, 0.0f, 0.0f); + obj_scene.g_clip_plane = clip_plane; } + else if (lights[LIGHT_REFLECT].get_type() == LIGHT_POINT) + obj_scene.g_clip_plane = { 0.0f, -1.0f, 0.0f, 9999.0f }; + else + obj_scene.g_clip_plane = { 0.0f, -1.0f, 0.0f, 0.0f }; - mat4 v63 = mat4_identity; - mat4 v64 = mat4_identity; + vec4 light_chara_ibl_direction; + vec4 light_chara_position; + lights[LIGHT_CHARA].get_ibl_direction(light_chara_ibl_direction); + lights[LIGHT_CHARA].get_position(light_chara_position); - lights[LIGHT_CHARA].get_ibl_direction(ibl_direction); - lights[LIGHT_CHARA].get_position(position); + mat4 normal_tangent_transforms = mat4_identity; - float_t length = vec3::length(*(vec3*)&ibl_direction); + float_t length = vec3::length(*(vec3*)&light_chara_ibl_direction); if (length >= 0.000001f) { - *(vec3*)&ibl_direction = *(vec3*)&ibl_direction * (1.0f / length); + vec3 ibl_direction = *(vec3*)&light_chara_ibl_direction * (1.0f / length); - length = vec3::length(*(vec3*)&position); + length = vec3::length(*(vec3*)&light_chara_position); if (length >= 0.000001f) { - *(vec3*)&position = *(vec3*)&position * (1.0f / length); + vec3 position = *(vec3*)&light_chara_position * (1.0f / length); - vec3 axis = vec3::cross(*(vec3*)&ibl_direction, *(vec3*)&position); + vec3 axis = vec3::cross(ibl_direction, position); length = vec3::length(axis); - float_t v52 = vec3::dot(*(vec3*)&position, *(vec3*)&ibl_direction); + float_t v52 = vec3::dot(ibl_direction, position); float_t angle = fabsf(atan2f(length, v52)); if (angle >= 0.01f && angle <= 3.131592653589793f) { if (length != 0.0f) axis *= 1.0f / length; - mat4_from_axis_angle(&axis, -angle, &v63); - mat4_from_axis_angle(&axis, -angle, &v64); + mat4_from_axis_angle(&axis, -angle, &normal_tangent_transforms); } } } - mat4_transpose(&v63, &v63); - shaders_ft.env_frag_set(26, v63.row0); - shaders_ft.env_frag_set(27, v63.row1); - shaders_ft.env_frag_set(28, v63.row2); - shaders_ft.env_frag_set(42, npr_spec_color); + obj_scene.set_g_normal_tangent_transforms(normal_tangent_transforms); + obj_scene.g_npr_cloth_spec_color = npr_cloth_spec_color; } static void light_get_direction_from_position(vec4* pos_dir, light_data* light, bool force) { if (force || light->get_type() == LIGHT_PARALLEL) { float_t length = vec3::length(*(vec3*)pos_dir); if (length <= 0.000001) - *(vec3*)pos_dir = { 0.0f, 1.0f, 0.0f }; + *(vec3*)pos_dir = vec3(0.0f, 1.0f, 0.0f); else - *(vec3*)pos_dir = *(vec3*)pos_dir * (1.0f / length); + *(vec3*)pos_dir *= 1.0f / length; pos_dir->w = 0.0f; } } diff --git a/src/CRE/light_param/light.hpp b/src/CRE/light_param/light.hpp index 1505cdcd..1c6b9933 100644 --- a/src/CRE/light_param/light.hpp +++ b/src/CRE/light_param/light.hpp @@ -21,66 +21,66 @@ struct light_data { float_t spot_exponent; float_t spot_cutoff; light_attenuation attenuation; - vec4 ibl_specular; - vec4 ibl_back; + vec4 ibl_color0; + vec4 ibl_color1; vec4 ibl_direction; light_tone_curve tone_curve; light_clip_plane clip_plane; light_data(); - light_type get_type(); + light_type get_type() const; void set_type(light_type value); - void get_ambient(vec4& value); + void get_ambient(vec4& value) const; void set_ambient(const vec4& value); void set_ambient(const vec4&& value); void set_ambient(const vec4& value, bool set[4]); void set_ambient(const vec4&& value, bool set[4]); - void get_diffuse(vec4& value); + void get_diffuse(vec4& value) const; void set_diffuse(const vec4& value); void set_diffuse(const vec4&& value); void set_diffuse(const vec4& value, bool set[4]); void set_diffuse(const vec4&& value, bool set[4]); - void get_specular(vec4& value); + void get_specular(vec4& value) const; void set_specular(const vec4& value); void set_specular(const vec4&& value); void set_specular(const vec4& value, bool set[4]); void set_specular(const vec4&& value, bool set[4]); - void get_position(vec3& value); + void get_position(vec3& value) const; void set_position(const vec3& value); void set_position(const vec3&& value); - void get_position(vec4& value); + void get_position(vec4& value) const; void set_position(const vec4& value); void set_position(const vec4&& value); - void get_spot_direction(vec3& value); + void get_spot_direction(vec3& value) const; void set_spot_direction(const vec3& value); void set_spot_direction(const vec3&& value); - float_t get_spot_exponent(); + float_t get_spot_exponent() const; void set_spot_exponent(float_t value); - float_t get_spot_cutoff(); + float_t get_spot_cutoff() const; void set_spot_cutoff(float_t value); - float_t get_constant(); + float_t get_constant() const; void set_constant(float_t value); - float_t get_linear(); + float_t get_linear() const; void set_linear(float_t value); - float_t get_quadratic(); + float_t get_quadratic() const; void set_quadratic(float_t value); - void get_attenuation(light_attenuation& value); + void get_attenuation(light_attenuation& value) const; void set_attenuation(const light_attenuation& value); void set_attenuation(const light_attenuation&& value); - void get_ibl_specular(vec4& value); - void set_ibl_specular(const vec4& value); - void set_ibl_specular(const vec4&& value); - void get_ibl_back(vec4& value); - void set_ibl_back(const vec4& value); - void set_ibl_back(const vec4&& value); - void get_ibl_direction(vec4& value); + void get_ibl_color0(vec4& value) const; + void set_ibl_color0(const vec4& value); + void set_ibl_color0(const vec4&& value); + void get_ibl_color1(vec4& value) const; + void set_ibl_color1(const vec4& value); + void set_ibl_color1(const vec4&& value); + void get_ibl_direction(vec4& value) const; void set_ibl_direction(const vec4& value); void set_ibl_direction(const vec4&& value); - void get_tone_curve(light_tone_curve& value); + void get_tone_curve(light_tone_curve& value) const; void set_tone_curve(const light_tone_curve& value); void set_tone_curve(const light_tone_curve&& value); - void get_clip_plane(light_clip_plane& value); + void get_clip_plane(light_clip_plane& value) const; void set_clip_plane(const light_clip_plane& value); void set_clip_plane(const light_clip_plane&& value); }; diff --git a/src/CRE/light_param/wind.cpp b/src/CRE/light_param/wind.cpp index a685b106..d58fa3fa 100644 --- a/src/CRE/light_param/wind.cpp +++ b/src/CRE/light_param/wind.cpp @@ -20,7 +20,7 @@ wind::~wind() { } -float_t wind::get_scale() { +float_t wind::get_scale() const { return scale; } @@ -28,7 +28,7 @@ void wind::set_scale(float_t value) { scale = value; } -float_t wind::get_cycle() { +float_t wind::get_cycle() const { return cycle; } @@ -36,7 +36,7 @@ void wind::set_cycle(float_t value) { cycle = value; } -void wind::get_rot(vec2* value) { +void wind::get_rot(vec2* value) const { value->x = rot_y; value->y = rot_z; } @@ -46,7 +46,7 @@ void wind::set_rot(vec2* value) { rot_z = value->y; } -float_t wind::get_rot_y() { +float_t wind::get_rot_y() const { return rot_y; } @@ -54,7 +54,7 @@ void wind::set_rot_y(float_t value) { rot_y = value; } -float_t wind::get_rot_z() { +float_t wind::get_rot_z() const { return rot_z; } @@ -62,7 +62,7 @@ void wind::set_rot_z(float_t value) { rot_z = value; } -float_t wind::get_bias() { +float_t wind::get_bias() const { return bias; } diff --git a/src/CRE/light_param/wind.hpp b/src/CRE/light_param/wind.hpp index c7a312e7..e7eb3b29 100644 --- a/src/CRE/light_param/wind.hpp +++ b/src/CRE/light_param/wind.hpp @@ -31,17 +31,17 @@ struct wind { wind(); ~wind(); - float_t get_scale(); + float_t get_scale() const; void set_scale(float_t value); - float_t get_cycle(); + float_t get_cycle() const; void set_cycle(float_t value); - void get_rot(vec2* value); + void get_rot(vec2* value) const; void set_rot(vec2* value); - float_t get_rot_y(); + float_t get_rot_y() const; void set_rot_y(float_t value); - float_t get_rot_z(); + float_t get_rot_z() const; void set_rot_z(float_t value); - float_t get_bias(); + float_t get_bias() const; void set_bias(float_t value); void reset(); }; diff --git a/src/CRE/mdata_manager.cpp b/src/CRE/mdata_manager.cpp index b585fb6e..8a6a9c76 100644 --- a/src/CRE/mdata_manager.cpp +++ b/src/CRE/mdata_manager.cpp @@ -81,12 +81,16 @@ void MdataMgr::Log(const char* fmt, ...) { log.append(1, '\n'); } -bool mdata_manager_append_task() { - return app::TaskWork::AppendTask(mdata_manager_get(), 0, "M_DATA_MANAGER"); +const std::list& MdataMgr::GetPrefixes() { + return prefixes; } -bool mdata_manager_free_task() { - return mdata_manager_get()->SetDest(); +bool mdata_manager_add_task() { + return app::TaskWork::AddTask(mdata_manager_get(), 0, "M_DATA_MANAGER"); +} + +bool mdata_manager_del_task() { + return mdata_manager_get()->DelTask(); } MdataMgr* mdata_manager_get() { diff --git a/src/CRE/mdata_manager.hpp b/src/CRE/mdata_manager.hpp index be673b42..5438a1fa 100644 --- a/src/CRE/mdata_manager.hpp +++ b/src/CRE/mdata_manager.hpp @@ -26,8 +26,9 @@ public: virtual void Basic() override; void Log(const char* fmt, ...); + const std::list& GetPrefixes(); }; -extern bool mdata_manager_append_task(); -extern bool mdata_manager_free_task(); +extern bool mdata_manager_add_task(); +extern bool mdata_manager_del_task(); extern MdataMgr* mdata_manager_get(); diff --git a/src/CRE/object.cpp b/src/CRE/object.cpp index 659cb052..f1bd0292 100644 --- a/src/CRE/object.cpp +++ b/src/CRE/object.cpp @@ -209,8 +209,8 @@ bool obj_mesh_vertex_buffer::load(obj_mesh& mesh, bool dynamic) { if (vertex_format & OBJ_VERTEX_BONE_DATA) { *(vec4*)d = vtx->bone_weight; d += 16; - *(vec4i*)d = vtx->bone_index; - d += 16; + *(vec4i16*)d = vtx->bone_index; + d += 8; } if (vertex_format & OBJ_VERTEX_UNKNOWN) { @@ -270,8 +270,7 @@ bool obj_mesh_vertex_buffer::load(obj_mesh& mesh, bool dynamic) { vec4_to_vec4u16(bone_weight, *(vec4u16*)d); d += 8; - vec4i bone_index = vtx->bone_index; - vec4i_to_vec4u16(bone_index, *(vec4u16*)d); + *(vec4i16*)d = vtx->bone_index; d += 8; } } @@ -848,6 +847,8 @@ void object_material_msgpack_read(const char* path, const char* set_name, if (msg.type != MSGPACK_MAP) return; + obj_set* set = handler->obj_set; + msgpack* add = msg.read_array("Add"); if (add) { std::vector ids; @@ -901,7 +902,7 @@ void object_material_msgpack_read(const char* path, const char* set_name, while (index / tex->mipmaps_count < tex->array_size); } - obj_set* set = handler->obj_set; + tex_db->sort(); size_t tex_id_num = txp_set->textures.size(); if (set->tex_id_num != tex_id_num) { @@ -935,10 +936,13 @@ void object_material_msgpack_read(const char* path, const char* set_name, uint32_t id = hash_string_murmurhash(name); + uint32_t* tex_id_data = set->tex_id_data; + uint32_t tex_id_num = set->tex_id_num; + txp* tex = 0; - for (texture_info& info : tex_db->texture) - if (id == info.id) { - tex = &txp_set->textures[&info - tex_db->texture.data()]; + for (uint32_t i = 0; i < tex_id_num; i++) + if (id == tex_id_data[i]) { + tex = &txp_set->textures[i]; break; } @@ -949,7 +953,7 @@ void object_material_msgpack_read(const char* path, const char* set_name, tex->has_cube_map = d.has_cube_map; tex->mipmaps_count = d.mipmaps_count; - tex->mipmaps.resize(0); + tex->mipmaps.clear(); tex->mipmaps.reserve((tex->has_cube_map ? 6LL : 1LL) * tex->mipmaps_count); int32_t index = 0; do @@ -1675,14 +1679,14 @@ inline obj_mesh_vertex_buffer* object_storage_get_obj_mesh_vertex_buffer(object_ return 0; } -GLuint obj_database_get_obj_set_texture(int32_t set, uint32_t tex_id) { - std::vector* textures = object_storage_get_obj_set_textures(set); +texture* obj_database_get_obj_set_texture(int32_t set, uint32_t tex_id) { + std::vector* textures = object_storage_get_obj_set_textures(set); if (!textures) - return -1; + return 0; obj_set_handler* handler = object_storage_get_obj_set_handler(set); if (!handler) - return -1; + return 0; std::pair* texture = handler->tex_id_data.data(); size_t length = handler->tex_id_data.size(); @@ -1698,10 +1702,10 @@ GLuint obj_database_get_obj_set_texture(int32_t set, uint32_t tex_id) { if (texture != handler->tex_id_data.data() + handler->tex_id_data.size() && tex_id == texture->first) return (*textures)[texture->second]; - return -1; + return 0; } -inline std::vector* object_storage_get_obj_set_textures(int32_t set) { +inline std::vector* object_storage_get_obj_set_textures(int32_t set) { obj_set_handler* handler = object_storage_get_obj_set_handler(set); if (handler) return &handler->gentex; @@ -1709,7 +1713,7 @@ inline std::vector* object_storage_get_obj_set_textures(int32_t set) { } int32_t object_storage_load_set(void* data, object_database* obj_db, const char* name) { - object_set_info* set_info = 0; + const object_set_info* set_info = 0; if (!obj_db->get_object_set_info(name, &set_info)) return 1; @@ -1722,9 +1726,9 @@ int32_t object_storage_load_set(void* data, object_database* obj_db, const char* return 1; } - std::string& archive_file_name = set_info->archive_file_name; - std::string& object_file_name = set_info->object_file_name; - std::string& texture_file_name = set_info->texture_file_name; + const std::string& archive_file_name = set_info->archive_file_name; + const std::string& object_file_name = set_info->object_file_name; + const std::string& texture_file_name = set_info->texture_file_name; if (!object_file_name.size() || !texture_file_name.size()) return 1; @@ -1746,7 +1750,7 @@ int32_t object_storage_load_set(void* data, object_database* obj_db, const char* } int32_t object_storage_load_set(void* data, object_database* obj_db, uint32_t set_id) { - object_set_info* set_info = 0; + const object_set_info* set_info = 0; if (!obj_db->get_object_set_info(set_id, &set_info)) return 1; @@ -1761,9 +1765,9 @@ int32_t object_storage_load_set(void* data, object_database* obj_db, uint32_t se handler->modern = false; - std::string& archive_file_name = set_info->archive_file_name; - std::string& object_file_name = set_info->object_file_name; - std::string& texture_file_name = set_info->texture_file_name; + const std::string& archive_file_name = set_info->archive_file_name; + const std::string& object_file_name = set_info->object_file_name; + const std::string& texture_file_name = set_info->texture_file_name; if (!object_file_name.size() || !texture_file_name.size()) return 1; @@ -1833,8 +1837,8 @@ bool object_storage_load_obj_set_check_not_read(uint32_t set_id, if (!data || !size) return false; - prj::shared_ptr& alloc = handler->alloc_handler; - alloc = prj::shared_ptr(new alloc_data); + prj::shared_ptr& alloc = handler->alloc_handler; + alloc = prj::shared_ptr(new prj::stack_allocator); obj_set* set = alloc->allocate(); handler->obj_set = set; @@ -1950,8 +1954,8 @@ bool object_storage_load_obj_set_check_not_read(uint32_t set_id, handler->name = name; - prj::shared_ptr& alloc = handler->alloc_handler; - alloc = prj::shared_ptr(new alloc_data); + prj::shared_ptr& alloc = handler->alloc_handler; + alloc = prj::shared_ptr(new prj::stack_allocator); obj_set* set = alloc->allocate(); handler->obj_set = set; @@ -1987,7 +1991,7 @@ bool object_storage_load_obj_set_check_not_read(uint32_t set_id, } inline void object_storage_unload_set(object_database* obj_db, const char* name) { - object_set_info* set_info = 0; + const object_set_info* set_info = 0; if (!obj_db->get_object_set_info(name, &set_info)) return; @@ -2125,7 +2129,7 @@ static void obj_set_handler_get_shader_index_texture_index(obj_set_handler* hand obj_material_texture_data& texture = k; uint32_t texture_id = texture.tex_index; - texture.tex_index = 0; + texture.tex_index = -1; texture.texture_index = 0; std::pair* tex_id_data = handler->tex_id_data.data(); @@ -2165,10 +2169,8 @@ static void obj_set_handler_index_buffer_free(obj_set_handler* handler) { handler->index_buffer_num = 0; } -static int32_t obj_set_tex_id_data_sort(void const* src1, void const* src2) { - std::pair* p1 = (std::pair*)src1; - std::pair* p2 = (std::pair*)src2; - return (int32_t)(p1->first - p2->first); +static size_t obj_set_tex_id_radix_index_func_id(std::pair* data, size_t index) { + return *(uint32_t*)&data[index].first; } static bool obj_set_handler_load_textures(obj_set_handler* handler, const void* data, bool big_endian) { @@ -2192,11 +2194,12 @@ static bool obj_set_handler_load_textures(obj_set_handler* handler, const void* texture** tex_data = handler->tex_data; for (int32_t i = 0; i < tex_num; i++) { handler->tex_id_data.push_back({ tex_id_data[i], i }); - handler->gentex.push_back(tex_data[i]->tex); + handler->gentex.push_back(tex_data[i]); } - quicksort_custom(handler->tex_id_data.data(), handler->tex_id_data.size(), - sizeof(std::pair), obj_set_tex_id_data_sort); + radix_sort_custom(handler->tex_id_data.data(), handler->tex_id_data.size(), + sizeof(std::pair), sizeof(uint32_t), + (radix_index_func)obj_set_tex_id_radix_index_func_id); return false; } @@ -2223,8 +2226,12 @@ static bool obj_set_handler_load_textures_modern(obj_set_handler* handler, texture** tex_data = handler->tex_data; for (uint32_t i = 0; i < tex_num; i++) { handler->tex_id_data.push_back({ tex_id_data[i], i }); - handler->gentex.push_back(tex_data[i]->tex); + handler->gentex.push_back(tex_data[i]); } + + radix_sort_custom(handler->tex_id_data.data(), handler->tex_id_data.size(), + sizeof(std::pair), sizeof(uint32_t), + (radix_index_func)obj_set_tex_id_radix_index_func_id); return false; } @@ -2275,7 +2282,7 @@ inline static size_t obj_vertex_format_get_vertex_size(obj_vertex_format format) if (format & OBJ_VERTEX_COLOR1) size += 16; if (format & OBJ_VERTEX_BONE_DATA) - size += 32; + size += 24; if (format & OBJ_VERTEX_UNKNOWN) size += 16; return size; diff --git a/src/CRE/object.hpp b/src/CRE/object.hpp index 1fd569c7..aea79186 100644 --- a/src/CRE/object.hpp +++ b/src/CRE/object.hpp @@ -74,11 +74,11 @@ struct obj_set_handler { bool obj_loaded; p_file_handler tex_file_handler; bool tex_loaded; - prj::shared_ptr alloc_handler; + prj::shared_ptr alloc_handler; obj_set* obj_set; std::vector> obj_id_data; uint32_t tex_num; - std::vector gentex; + std::vector gentex; std::vector> tex_id_data; texture** tex_data; int32_t set_id; @@ -130,8 +130,8 @@ extern obj_index_buffer* object_storage_get_obj_index_buffers(uint32_t set_id); extern obj_mesh_index_buffer* object_storage_get_obj_mesh_index_buffer(object_info obj_info); extern obj_vertex_buffer* object_storage_get_obj_vertex_buffers(uint32_t set_id); extern obj_mesh_vertex_buffer* object_storage_get_obj_mesh_vertex_buffer(object_info obj_info); -extern GLuint obj_database_get_obj_set_texture(int32_t set, uint32_t tex_id); -extern std::vector* object_storage_get_obj_set_textures(int32_t set); +extern texture* obj_database_get_obj_set_texture(int32_t set, uint32_t tex_id); +extern std::vector* object_storage_get_obj_set_textures(int32_t set); extern int32_t object_storage_load_set(void* data, object_database* obj_db, const char* name); extern int32_t object_storage_load_set(void* data, object_database* obj_db, uint32_t set_id); extern int32_t object_storage_load_set_hash(void* data, uint32_t hash); diff --git a/src/CRE/ogg_vorbis.hpp b/src/CRE/ogg_vorbis.hpp index 65030dc3..d92ec85d 100644 --- a/src/CRE/ogg_vorbis.hpp +++ b/src/CRE/ogg_vorbis.hpp @@ -6,7 +6,7 @@ #pragma once #include "../KKdLib/default.hpp" -#include "../CRE/waitable_timer.hpp" +#include "../KKdLib/waitable_timer.hpp" #include "lock.hpp" #include "sound.hpp" #include diff --git a/src/CRE/post_process.cpp b/src/CRE/post_process.cpp index ee83d7b8..334b1ab0 100644 --- a/src/CRE/post_process.cpp +++ b/src/CRE/post_process.cpp @@ -18,67 +18,14 @@ extern bool task_stage_is_modern; extern render_context* rctx_ptr; -post_process::post_process() : ssaa(), mlaa(), ss_alpha_mask(), aet_back_tex(), render_textures_data(), -movie_textures_data(), texture_counter(), lens_shaft_query(), lens_flare_query(), lens_shaft_query_data(), -lens_flare_query_data(), lens_flare_query_index(), lens_flare_texture(), lens_shaft_texture(), -lens_ghost_texture(), lens_ghost_count(), lens_flare_pos(), lens_shaft_scale(), lens_shaft_inv_scale(), -lens_flare_power(), field_A10(), lens_flare_appear_power(), render_width(), render_height(), view_point(), +post_process::post_process() : ssaa(), mlaa(), ss_alpha_mask(), aet_back_tex(), +render_textures_data(), movie_textures_data(), aet_back(), texture_counter(), lens_shaft_query(), +lens_flare_query(), lens_shaft_query_data(), lens_flare_query_data(), lens_flare_query_index(), +lens_flare_texture(), lens_shaft_texture(), lens_ghost_texture(), lens_ghost_count(), +lens_flare_pos(), lens_shaft_scale(), lens_shaft_inv_scale(), lens_flare_power(), +field_A10(), lens_flare_appear_power(), render_width(), render_height(), view_point(), interest(), view_point_prev(), interest_prev(), reset_exposure(), sprite_width(), sprite_height(), screen_x_offset(), screen_y_offset(), screen_width(), screen_height(), mag_filter() { - static const char* query_vert_shader = - "#version 430 core\n" - "layout(location = 0) in vec4 a_position;\n" - "\n" - "uniform mat4 mat;\n" - "uniform float scale;\n" - "\n" - "void main() {\n" - " vec4 pos = a_position;\n" - " pos.xy *= scale;\n" - " gl_Position = mat * pos;\n" - "}\n"; - - static const char* query_frag_shader = - "#version 430 core\n" - "layout(location = 0) out vec4 result;\n" - "\n" - "void main() {\n" - " result = vec4(0.0);\n" - "}\n"; - - static const char* alpha_layer_vert_shader = - "#version 430 core\n" - "out VertexData {\n" - " vec2 texcoord;\n" - "} result;\n" - "\n" - "void main() {\n" - " gl_Position.x = -1.0 + float(gl_VertexID / 2) * 4.0;\n" - " gl_Position.y = 1.0 - float(gl_VertexID % 2) * 4.0;\n" - " gl_Position.z = 0.0;\n" - " gl_Position.w = 1.0;\n" - " result.texcoord = gl_Position.xy * 0.5 + 0.5;\n" - "}\n"; - - static const char* alpha_layer_frag_shader = - "#version 430\n" - "layout(binding = 0) uniform sampler2D g_layerd_color;\n" - "layout(binding = 1) uniform sampler2D g_base_color;\n" - "\n" - "layout(location = 0) uniform float g_opacity;\n" - "\n" - "in VertexData{\n" - " vec2 texcoord;\n" - "}frg;\n" - "\n" - "layout(location = 0) out vec4 result;\n" - "\n" - "void main(){\n" - " vec4 cl = textureLod(g_layerd_color, frg.texcoord, 0.f);\n" - " vec4 cb = textureLod(g_base_color, frg.texcoord, 0.f);\n" - " result = mix(cb, cl, g_opacity.x);\n" - "}\n"; - aa = new post_process_aa(); blur = new post_process_blur(); dof = new post_process_dof(); @@ -124,31 +71,13 @@ screen_x_offset(), screen_y_offset(), screen_width(), screen_height(), mag_filte glGenVertexArrays(1, &lens_ghost_vao); gl_state_bind_vertex_array(lens_ghost_vao); - const float_t lens_ghost_verts[] = { - -0.5f, -0.5f, 0.0f, 0.0f, - 0.5f, -0.5f, 0.5f, 0.0f, - -0.5f, 0.5f, 0.0f, 0.5f, - 0.5f, 0.5f, 0.5f, 0.5f, - -0.5f, -0.5f, 0.5f, 0.0f, - 0.5f, -0.5f, 1.0f, 0.0f, - -0.5f, 0.5f, 0.5f, 0.5f, - 0.5f, 0.5f, 1.0f, 0.5f, - -0.5f, -0.5f, 0.0f, 0.5f, - 0.5f, -0.5f, 0.5f, 0.5f, - -0.5f, 0.5f, 0.0f, 1.0f, - 0.5f, 0.5f, 0.5f, 1.0f, - -0.5f, -0.5f, 0.5f, 0.5f, - 0.5f, -0.5f, 1.0f, 0.5f, - -0.5f, 0.5f, 0.5f, 1.0f, - 0.5f, 0.5f, 1.0f, 1.0f, - }; - glGenBuffers(1, &lens_ghost_vbo); gl_state_bind_array_buffer(lens_ghost_vbo, true); if (GLAD_GL_VERSION_4_4) - glBufferStorage(GL_ARRAY_BUFFER, sizeof(lens_ghost_verts), lens_ghost_verts, 0); + glBufferStorage(GL_ARRAY_BUFFER, sizeof(float_t) * 5 * (6 * 16), + 0, GL_DYNAMIC_STORAGE_BIT | GL_MAP_WRITE_BIT); else - glBufferData(GL_ARRAY_BUFFER, sizeof(lens_ghost_verts), lens_ghost_verts, GL_STATIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(float_t) * 5 * (6 * 16), 0, GL_DYNAMIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 16, (void*)0); // Pos @@ -159,22 +88,14 @@ screen_x_offset(), screen_y_offset(), screen_width(), screen_height(), mag_filte gl_state_bind_array_buffer(0); gl_state_bind_vertex_array(0); - shader_glsl_param param = {}; - param.name = "Exposure Chara"; - query_shader.load(query_vert_shader, - query_frag_shader, 0, ¶m); - - param = {}; - param.name = "Alpha Layer"; - alpha_layer_shader.load(alpha_layer_vert_shader, - alpha_layer_frag_shader, 0, ¶m); - if (!lens_shaft_query[0]) glGenQueries(3, lens_shaft_query); if (!lens_flare_query[0]) glGenQueries(3, lens_flare_query); + sun_quad_ubo.Create(sizeof(sun_quad_shader_data)); + reset(); } @@ -232,9 +153,6 @@ post_process::~post_process() { lens_ghost_vbo = 0; } - query_shader.unload(); - alpha_layer_shader.unload(); - if (lens_shaft_query[0]) { glDeleteQueries(3, lens_shaft_query); lens_shaft_query[0] = 0; @@ -244,6 +162,8 @@ post_process::~post_process() { glDeleteQueries(3, lens_flare_query); lens_flare_query[0] = 0; } + + sun_quad_ubo.Destroy(); } void post_process::apply(camera* cam, texture* light_proj_tex, int32_t npr_param) { @@ -258,8 +178,6 @@ void post_process::apply(camera* cam, texture* light_proj_tex, int32_t npr_param draw_lens_ghost(&rend_texture); - shaders_ft.state_matrix_set_mvp(mat4_identity, mat4_identity, mat4_identity); - blur->get_blur(&rend_texture); exposure->get_exposure(cam, render_width, render_height, reset_exposure, blur->tex[4].color_texture->tex, blur->tex[2].color_texture->tex); @@ -285,8 +203,8 @@ void post_process::apply(camera* cam, texture* light_proj_tex, int32_t npr_param glViewport(0, 0, t->width, t->height); gl_state_active_bind_texture_2d(0, rend_texture.color_texture->tex); gl_state_bind_sampler(0, samplers[0]); - shaders_ft.set(SHADER_FT_REDUCE); - render_texture::draw_params(&shaders_ft, render_width, render_height); + shaders_ft.set_opengl_shader(SHADER_FT_REDUCE); + render_texture::draw_quad(&shaders_ft, render_width, render_height); } fbo::blit(rend_texture.fbos[0], post_texture.fbos[0], @@ -300,8 +218,8 @@ void post_process::apply(camera* cam, texture* light_proj_tex, int32_t npr_param gl_state_bind_sampler(0, samplers[0]); uniform_value[U_ALPHA_MASK] = ss_alpha_mask ? 1 : 0; uniform_value[U_REDUCE] = 0; - shaders_ft.set(SHADER_FT_REDUCE); - render_texture::draw_params(&shaders_ft, render_width, render_height); + shaders_ft.set_opengl_shader(SHADER_FT_REDUCE); + render_texture::draw_quad(&shaders_ft, render_width, render_height); uniform_value[U_ALPHA_MASK] = 0; } else { @@ -314,21 +232,27 @@ void post_process::apply(camera* cam, texture* light_proj_tex, int32_t npr_param switch (mag_filter) { case POST_PROCESS_MAG_FILTER_NEAREST: case POST_PROCESS_MAG_FILTER_BILINEAR: - case POST_PROCESS_MAG_FILTER_SHARPEN_5_TAP: - case POST_PROCESS_MAG_FILTER_SHARPEN_4_TAP: - case POST_PROCESS_MAG_FILTER_CONE_4_TAP: - case POST_PROCESS_MAG_FILTER_CONE_2_TAP: - uniform_value[U_MAGNIFY] = mag_filter; - break; default: uniform_value[U_MAGNIFY] = 0; break; + case POST_PROCESS_MAG_FILTER_SHARPEN_5_TAP: + uniform_value[U_MAGNIFY] = 2; + break; + case POST_PROCESS_MAG_FILTER_SHARPEN_4_TAP: + uniform_value[U_MAGNIFY] = 3; + break; + case POST_PROCESS_MAG_FILTER_CONE_4_TAP: + uniform_value[U_MAGNIFY] = 4; + break; + case POST_PROCESS_MAG_FILTER_CONE_2_TAP: + uniform_value[U_MAGNIFY] = 5; + break; } - shaders_ft.set(SHADER_FT_MAGNIFY); - render_texture::draw_params(&shaders_ft, render_width, render_height); + shaders_ft.set_opengl_shader(SHADER_FT_MAGNIFY); + render_texture::draw_quad(&shaders_ft, render_width, render_height); } - shader::unbind(); + shader_opengl::unbind(); for (int32_t i = 0; i < 8; i++) gl_state_bind_sampler(i, 0); @@ -374,8 +298,8 @@ void post_process::draw_lens_flare(camera* cam) { if (!lens_flare_texture) return; - GLuint tex = obj_database_get_obj_set_texture(5, 4549); - if (tex == -1) + texture* tex = obj_database_get_obj_set_texture(5, 4549); + if (!tex) return; float_t v5 = tanf((float_t)(cam->get_fov() * DEG_TO_RAD) * 0.5f); @@ -383,18 +307,18 @@ void post_process::draw_lens_flare(camera* cam) { light_data* data = &set->lights[LIGHT_SUN]; vec3 position; - vec4 v51; + vec4 emission; if (data->get_type() == LIGHT_PARALLEL) { data->get_position(position); - data->get_diffuse(v51); + data->get_diffuse(emission); } else { set->lights[LIGHT_STAGE].get_position(position); position = vec3::normalize(position) * 500.0f; - set->lights[LIGHT_STAGE].get_ibl_specular(v51); + set->lights[LIGHT_STAGE].get_ibl_color0(emission); data->set_type(LIGHT_PARALLEL); data->set_position(position); - data->set_diffuse(v51); + data->set_diffuse(emission); } vec4 v44; @@ -423,12 +347,15 @@ void post_process::draw_lens_flare(camera* cam) { float_t v23 = ((float_t)render_width * (flt_1411ACB8C / v5)) * (v22 * v22 * 0.5f) * ((float_t)render_height * (flt_1411ACB8C / v5)); float_t v24 = vec3::distance(position, view_point) * flt_1411ACB84; - v51 *= 1.0f / (float_t)(1.0f - cosf((float_t)(3.0 * DEG_TO_RAD))); + emission *= 1.0f / (float_t)(1.0f - cosf((float_t)(3.0 * DEG_TO_RAD))); - shaders_ft.state_material_set_emission(false, v51); - gl_state_active_bind_texture_2d(0, tex); - shaders_ft.set(SHADER_FT_SUN); + sun_quad_shader_data shader_data = {}; + shader_data.g_emission = emission; + + gl_state_active_bind_texture_2d(0, tex->tex); + shaders_ft.set_opengl_shader(SHADER_FT_SUN); gl_state_bind_vertex_array(query_vao); + sun_quad_ubo.Bind(0); int32_t query_index = (lens_flare_query_index + 1) % 3; lens_flare_query_index = query_index; @@ -449,8 +376,14 @@ void post_process::draw_lens_flare(camera* cam) { mat4_clear_rot(&mat, &mat); mat4_scale_rot(&mat, v24 * 0.2f, &mat); + mat4_transpose(&mat, &mat); + shader_data.g_transform[0] = mat.row0; + shader_data.g_transform[1] = mat.row1; + shader_data.g_transform[2] = mat.row2; + shader_data.g_transform[3] = mat.row3; + sun_quad_ubo.WriteMapMemory(shader_data); + glBeginQuery(GL_SAMPLES_PASSED, lens_shaft_query[next_query_index]); - shaders_ft.state_matrix_set_modelview(mat, true); shaders_ft.draw_arrays(GL_TRIANGLE_STRIP, 0, 4); glEndQuery(GL_SAMPLES_PASSED); @@ -458,8 +391,14 @@ void post_process::draw_lens_flare(camera* cam) { mat4_clear_rot(&mat, &mat); mat4_scale_rot(&mat, v24 * 2.0f, &mat); + mat4_transpose(&mat, &mat); + shader_data.g_transform[0] = mat.row0; + shader_data.g_transform[1] = mat.row1; + shader_data.g_transform[2] = mat.row2; + shader_data.g_transform[3] = mat.row3; + sun_quad_ubo.WriteMapMemory(shader_data); + glBeginQuery(GL_SAMPLES_PASSED, lens_flare_query[next_query_index]); - shaders_ft.state_matrix_set_modelview(mat, true); shaders_ft.draw_arrays(GL_TRIANGLE_STRIP, 0, 4); glEndQuery(GL_SAMPLES_PASSED); @@ -481,7 +420,7 @@ void post_process::draw_lens_flare(camera* cam) { glGetQueryObjectuiv(lens_flare_query[query_index], GL_QUERY_RESULT, &lens_flare_query_data[query_index]); } - if (v51.y + v51.x + v51.z > 0.0f) { + if (emission.x + emission.y + emission.z > 0.0f) { gl_state_enable_blend(); gl_state_set_blend_func(GL_ONE, GL_ONE); gl_state_set_depth_mask(GL_FALSE); @@ -490,7 +429,6 @@ void post_process::draw_lens_flare(camera* cam) { mat4_clear_rot(&mat, &mat); mat4_scale_rot(&mat, v24 * 1.1f, &mat); - shaders_ft.state_matrix_set_modelview(mat, true); shaders_ft.draw_arrays(GL_TRIANGLE_STRIP, 0, 4); gl_state_disable_blend(); @@ -543,6 +481,36 @@ void post_process::draw_lens_flare(camera* cam) { } } +static void make_ghost_quad(uint8_t flags, float_t opacity, mat4* mat, float_t*& data) { + const float_t x0 = flags & 0x01 ? 0.5f : 0.0f; + const float_t x1 = flags & 0x01 ? 1.0f : 0.5f; + const float_t y0 = flags & 0x02 ? 0.5f : 0.0f; + const float_t y1 = flags & 0x02 ? 1.0f : 0.5f; + + vec4 p0 = { -0.5f, -0.5f, x0, y0 }; + vec4 p1 = { 0.5f, -0.5f, x1, y0 }; + vec4 p2 = { -0.5f, 0.5f, x0, y1 }; + vec4 p3 = { 0.5f, 0.5f, x1, y1 }; + mat4_mult_vec2_trans(mat, (vec2*)&p0, (vec2*)&p0); + mat4_mult_vec2_trans(mat, (vec2*)&p1, (vec2*)&p1); + mat4_mult_vec2_trans(mat, (vec2*)&p2, (vec2*)&p2); + mat4_mult_vec2_trans(mat, (vec2*)&p3, (vec2*)&p3); + + *(vec4*)&data[0] = p0; + data[4] = opacity; + *(vec4*)&data[5] = p1; + data[9] = opacity; + *(vec4*)&data[10] = p2; + data[14] = opacity; + *(vec4*)&data[15] = p1; + data[19] = opacity; + *(vec4*)&data[20] = p2; + data[24] = opacity; + *(vec4*)&data[25] = p3; + data[29] = opacity; + data += 30; +} + void post_process::draw_lens_ghost(render_texture* rt) { static const float_t v13[16] = { -0.70f, -0.30f, 0.35f, 0.50f, @@ -585,24 +553,28 @@ void post_process::draw_lens_ghost(render_texture* rt) { const float_t angle_sin = sinf(angle); const float_t angle_cos = cosf(angle); - glViewport(0, 0, rt->color_texture->width, rt->color_texture->height); - rt->bind(); - gl_state_enable_blend(); - gl_state_set_blend_func(GL_ONE, GL_ONE); - uniform_value[U_REDUCE] = 4; - shaders_ft.set(SHADER_FT_REDUCE); + float_t* data; + if (GLAD_GL_VERSION_4_5) { + data = (float_t*)glMapNamedBuffer(lens_ghost_vbo, GL_WRITE_ONLY); + if (!data) { + glUnmapNamedBuffer(lens_ghost_vbo); + return; + } + } + else { + gl_state_bind_array_buffer(lens_ghost_vbo); + data = (float_t*)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); + if (!data) { + glUnmapBuffer(GL_ARRAY_BUFFER); + gl_state_bind_array_buffer(0); + return; + } + } - mat4 proj; - mat4_ortho(0.0, 1.0, 0.0, 1.0, 0.0, 1.0, &proj); - shaders_ft.state_matrix_set_projection(proj, false); - - gl_state_active_bind_texture_2d(0, lens_ghost_texture); - - gl_state_bind_vertex_array(lens_ghost_vao); const float_t lens_ghost = tone_map->data.lens_ghost; const int32_t lens_ghost_count = this->lens_ghost_count; for (int32_t i = 0; i < lens_ghost_count; i++) { - shaders_ft.local_frag_set(3, 0.0f, 0.0f, 0.0f, v9 * v14[i] * lens_ghost); + float_t opacity = v9 * v14[i] * lens_ghost; float_t scale = (v9a * 0.03f + 0.02f) * v15[i]; @@ -610,26 +582,32 @@ void post_process::draw_lens_ghost(render_texture* rt) { mat4_translate(v13[i] * v7 + 0.5f, v13[i] * v8 + 0.5f, 0.0f, &mat); mat4_scale_rot(&mat, scale, scale * aspect, 1.0f, &mat); mat4_rotate_z_mult_sin_cos(&mat, angle_sin, angle_cos, &mat); - shaders_ft.state_matrix_set_modelview(mat, true); - shaders_ft.draw_arrays(GL_TRIANGLE_STRIP, i % 4 * 4, 4); + make_ghost_quad((uint8_t)(i & 0x03), opacity, &mat, data); } + + if (GLAD_GL_VERSION_4_5) + glUnmapNamedBuffer(lens_ghost_vbo); + else { + glUnmapBuffer(GL_ARRAY_BUFFER); + gl_state_bind_array_buffer(0); + } + + glViewport(0, 0, rt->color_texture->width, rt->color_texture->height); + rt->bind(); + gl_state_enable_blend(); + gl_state_set_blend_func(GL_ONE, GL_ONE); + + uniform_value[U_REDUCE] = 4; + shaders_ft.set_opengl_shader(SHADER_FT_REDUCE); + gl_state_active_bind_texture_2d(0, lens_ghost_texture->tex); + gl_state_bind_vertex_array(lens_ghost_vao); + shaders_ft.draw_arrays(GL_TRIANGLES, 0, (GLsizei)(lens_ghost_count * 6LL)); gl_state_bind_vertex_array(0); gl_state_disable_blend(); gl_state_set_blend_func(GL_ONE, GL_ZERO); } -void post_process::draw_query_samples(GLuint query, float_t scale, mat4& mat) { - glBeginQuery(GL_SAMPLES_PASSED, query); - gl_state_bind_vertex_array(query_vao); - query_shader.use(); - query_shader.set("mat", false, mat); - query_shader.set("scale", scale); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - gl_state_bind_vertex_array(0); - glEndQuery(GL_SAMPLES_PASSED); -} - void post_process::init_fbo(int32_t render_width, int32_t render_height, int32_t sprite_width, int32_t sprite_height, int32_t screen_width, int32_t screen_height) { if (this->render_width == render_width && this->render_height == render_height @@ -651,7 +629,7 @@ void post_process::init_fbo(int32_t render_width, int32_t render_height, aet_back_texture.set_color_depth_textures(aet_back_tex->tex, 0, rend_texture.depth_texture->tex); pre_texture.init(render_width, render_height, 0, GL_RGBA16F, 0); post_texture.init(render_width, render_height, 0, GL_RGBA16F, 0); - alpha_layer_texture.init(render_width, render_height, 0, GL_RGBA16F, GL_DEPTH_COMPONENT32F); + transparency_texture.init(render_width, render_height, 0, GL_RGBA16F, GL_DEPTH_COMPONENT32F); gl_state_bind_texture_2d(rend_texture.depth_texture->tex); GLint swizzle[] = { GL_RED, GL_RED, GL_RED, GL_ONE }; glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle); diff --git a/src/CRE/post_process.hpp b/src/CRE/post_process.hpp index 7cdca910..8f4d0de7 100644 --- a/src/CRE/post_process.hpp +++ b/src/CRE/post_process.hpp @@ -7,6 +7,7 @@ #include "../KKdLib/default.hpp" #include "../KKdLib/vec.hpp" +#include "GL/uniform_buffer.hpp" #include "post_process/aa.hpp" #include "post_process/blur.hpp" #include "post_process/dof.hpp" @@ -24,6 +25,11 @@ enum post_process_mag_filter_type { POST_PROCESS_MAG_FILTER_MAX = 6, }; +struct sun_quad_shader_data { + vec4 g_transform[4]; + vec4 g_emission; +}; + struct post_process { bool ssaa; bool mlaa; @@ -36,7 +42,7 @@ struct post_process { render_texture pre_texture; render_texture post_texture; render_texture fbo_texture; - render_texture alpha_layer_texture; + render_texture transparency_texture; render_texture screen_texture; texture* render_textures_data[16]; render_texture render_textures[16]; @@ -49,21 +55,20 @@ struct post_process { post_process_exposure* exposure; post_process_tone_map* tone_map; GLuint samplers[2]; - shader_glsl query_shader; GLuint query_vao; GLuint query_vbo; GLuint lens_ghost_vao; GLuint lens_ghost_vbo; - shader_glsl alpha_layer_shader; + GL::UniformBuffer sun_quad_ubo; int32_t texture_counter; GLuint lens_shaft_query[3]; GLuint lens_flare_query[3]; GLuint lens_shaft_query_data[3]; GLuint lens_flare_query_data[3]; int32_t lens_flare_query_index; - GLuint lens_flare_texture; - GLuint lens_shaft_texture; - GLuint lens_ghost_texture; + texture* lens_flare_texture; + texture* lens_shaft_texture; + texture* lens_ghost_texture; int32_t lens_ghost_count; vec3 lens_flare_pos; float_t lens_shaft_scale; @@ -95,7 +100,6 @@ struct post_process { void ctrl(camera* cam); void draw_lens_flare(camera* cam); void draw_lens_ghost(render_texture* rt); - void draw_query_samples(GLuint query, float_t scale, mat4& mat); void init_fbo(int32_t render_width, int32_t render_height, int32_t sprite_width, int32_t sprite_height, int32_t screen_width, int32_t screen_height); int32_t movie_texture_set(texture* movie_texture); diff --git a/src/CRE/post_process/aa.cpp b/src/CRE/post_process/aa.cpp index db600efa..cdc83a53 100644 --- a/src/CRE/post_process/aa.cpp +++ b/src/CRE/post_process/aa.cpp @@ -35,37 +35,31 @@ post_process_aa::~post_process_aa() { void post_process_aa::apply_mlaa(render_texture* rt, render_texture* buf_rt, GLuint* samplers, int32_t ss_alpha_mask) { - mlaa_buffer->bind(); + mlaa_buffer.bind(); gl_state_active_bind_texture_2d(0, rt->color_texture->tex); gl_state_bind_sampler(0, samplers[1]); - //uniform_value[U_MLAA] = 0; - //shaders_ft.set(SHADER_FT_MLAA); - uniform_value[U_ALPHA_MASK] = 0; - shaders_ft.set(SHADER_FT_MLAA_EDGE); - render_texture::draw_params(&shaders_ft, width, height); + uniform_value[U_MLAA] = 0; + shaders_ft.set_opengl_shader(SHADER_FT_MLAA); + render_texture::draw_quad(&shaders_ft, width, height); - mlaa_buffer[1].bind(); - gl_state_active_bind_texture_2d(0, mlaa_buffer[0].color_texture->tex); + temp_buffer.bind(); + gl_state_active_bind_texture_2d(0, mlaa_buffer.color_texture->tex); gl_state_active_bind_texture_2d(1, mlaa_area_texture); gl_state_bind_sampler(0, samplers[0]); gl_state_bind_sampler(1, samplers[1]); - //uniform_value[U_MLAA_SEARCH] = 2; - //uniform_value[U_MLAA] = 1; - //shaders_ft.set(SHADER_FT_MLAA); - uniform_value[U_MLAA_SEARCH] = 4; - shaders_ft.set(SHADER_FT_MLAA_AREA); - render_texture::draw_params(&shaders_ft, width, height); + uniform_value[U_MLAA] = 1; + uniform_value[U_MLAA_SEARCH] = 2; + shaders_ft.set_opengl_shader(SHADER_FT_MLAA); + render_texture::draw_quad(&shaders_ft, width, height); buf_rt->bind(); gl_state_active_bind_texture_2d(0, rt->color_texture->tex); - gl_state_active_bind_texture_2d(1, mlaa_buffer[1].color_texture->tex); + gl_state_active_bind_texture_2d(1, temp_buffer.color_texture->tex); gl_state_bind_sampler(0, samplers[1]); - //uniform_value[U_MLAA] = 2; - //uniform_value[U_ALPHA_MASK] = ss_alpha_mask ? 1 : 0; - //shaders_ft.set(SHADER_FT_MLAA); + uniform_value[U_MLAA] = 2; uniform_value[U_ALPHA_MASK] = ss_alpha_mask ? 1 : 0; - shaders_ft.set(SHADER_FT_MLAA_BLEND); - render_texture::draw_params(&shaders_ft, width, height); + shaders_ft.set_opengl_shader(SHADER_FT_MLAA); + render_texture::draw_quad(&shaders_ft, width, height); uniform_value[U_ALPHA_MASK] = 0; gl_state_active_bind_texture_2d(0, 0); gl_state_active_bind_texture_2d(1, 0); @@ -82,8 +76,8 @@ void post_process_aa::init_fbo(int32_t width, int32_t height) { this->width = max_def(width, 1); this->height = max_def(height, 1); - mlaa_buffer[0].init(this->width, this->height, 0, GL_RGBA8, 0); - mlaa_buffer[1].init(this->width, this->height, 0, GL_RGBA8, 0); + mlaa_buffer.init(this->width, this->height, 0, GL_RGBA8, 0); + temp_buffer.init(this->width, this->height, 0, GL_RGBA8, 0); } static void post_process_aa_generate_area_texture(post_process_aa* aa) { diff --git a/src/CRE/post_process/aa.hpp b/src/CRE/post_process/aa.hpp index d9c314b5..20f3969b 100644 --- a/src/CRE/post_process/aa.hpp +++ b/src/CRE/post_process/aa.hpp @@ -13,7 +13,8 @@ struct post_process_aa { int32_t width; int32_t height; - render_texture mlaa_buffer[2]; + render_texture mlaa_buffer; + render_texture temp_buffer; GLuint mlaa_area_texture; post_process_aa(); diff --git a/src/CRE/post_process/blur.cpp b/src/CRE/post_process/blur.cpp index db97c507..dd1ed5a9 100644 --- a/src/CRE/post_process/blur.cpp +++ b/src/CRE/post_process/blur.cpp @@ -7,8 +7,13 @@ #include "../fbo.hpp" #include "../gl_state.hpp" #include "../post_process.hpp" +#include "../render_context.hpp" #include "../shader_ft.hpp" +struct gaussian_coef_shader_data { + vec4 g_coef[8]; +}; + static void post_process_blur_radius_calculate(post_process_blur* blur); static void post_process_blur_radius_calculate_gaussian_kernel(float_t* gaussian_kernel, float_t radius, int32_t stride, int32_t offset); @@ -19,14 +24,16 @@ width(), height(), width_down(), height_down(), tex_down(), count_down() { tex[1].init(128, 72, 0, GL_RGBA16F, 0); tex[2].init(64, 36, 0, GL_RGBA16F, 0); tex[3].init(32, 18, 0, GL_RGBA16F, 0); - tex[4].init(8, 8, 0, GL_R32F, 0); + tex[4].init(8, 8, 0, GL_RGBA16F, 0); tex[5].init(256, 144, 0, GL_RGBA16F, 0); + gaussian_coef_ubo.Create(sizeof(gaussian_coef_shader_data)); } post_process_blur:: ~post_process_blur() { if (!this) return; + gaussian_coef_ubo.Destroy(); for (int32_t i = 0; i < count_down; i++) tex_down[i].free(); free_def(tex_down); @@ -37,93 +44,91 @@ post_process_blur:: ~post_process_blur() { void post_process_blur::get_blur(render_texture* rt) { uniform_value[U_ALPHA_MASK] = 0; + vec3 intensity = data.intensity; + vec3* gauss = data.gauss; + + gaussian_coef_shader_data gaussian_coef = {}; + for (int32_t i = 0; i < POST_PROCESS_BLUR_GAUSSIAN_KERNEL_SIZE && i < 8; i++) { + vec3 coef = gauss[i] * intensity; + gaussian_coef.g_coef[i] = { coef.x, coef.y, coef.z, 0.0f }; + } + + gaussian_coef_ubo.WriteMapMemory(gaussian_coef); + int32_t i = 0; if (count_down > 0) { uniform_value[U_REDUCE] = 1; - shaders_ft.set(SHADER_FT_REDUCE); + shaders_ft.set_opengl_shader(SHADER_FT_REDUCE); for (; i < count_down; i++) { glViewport(0, 0, width_down[i], height_down[i]); tex_down[i].bind(); gl_state_active_bind_texture_2d(0, i ? tex_down[i - 1].color_texture->tex : rt->color_texture->tex); - render_texture::draw_params(&shaders_ft, width_down[i], height_down[i]); + render_texture::draw_quad(&shaders_ft, width_down[i], height_down[i]); } i--; } uniform_value[U_REDUCE] = 3; - shaders_ft.set(SHADER_FT_REDUCE); - shaders_ft.local_frag_set(2, 1.1f, 1.1f, 1.1f, 0.0f); + shaders_ft.set_opengl_shader(SHADER_FT_REDUCE); glViewport(0, 0, 256, 144); tex[0].bind(); gl_state_active_bind_texture_2d(0, count_down > 0 ? tex_down[i].color_texture->tex : rt->color_texture->tex); - render_texture::draw_params(&shaders_ft, width, height); + render_texture::draw_quad(&shaders_ft, width, height, 1.0f, 1.1f, 1.1f, 1.1f, 0.0f); - //uniform_value[U_GAUSS] = 1; - //shaders_ft.set(SHADER_FT_GAUSS); - shaders_ft.set(SHADER_FT_GAUSS_CONE); + uniform_value[U_GAUSS] = 1; + shaders_ft.set_opengl_shader(SHADER_FT_GAUSS); glViewport(0, 0, 256, 144); tex[5].bind(); gl_state_active_bind_texture_2d(0, tex[0].color_texture->tex); - render_texture::draw_params(&shaders_ft, 256, 144, - 1.0f, data.intensity.x * 0.5f, data.intensity.y * 0.5f, data.intensity.z * 0.5f, 1.0f); + render_texture::draw_quad(&shaders_ft, 256, 144, 1.0f, + data.intensity.x * 0.5f, data.intensity.y * 0.5f, data.intensity.z * 0.5f, 1.0f); uniform_value[U_REDUCE] = 1; - shaders_ft.set(SHADER_FT_REDUCE); + shaders_ft.set_opengl_shader(SHADER_FT_REDUCE); glViewport(0, 0, 128, 72); tex[1].bind(); gl_state_active_bind_texture_2d(0, tex[0].color_texture->tex); - render_texture::draw_params(&shaders_ft, 256, 144, 0.75f, 1.0f, 1.0f, 1.0f, 1.0f); + render_texture::draw_quad(&shaders_ft, 256, 144); uniform_value[U_REDUCE] = 1; - shaders_ft.set(SHADER_FT_REDUCE); + shaders_ft.set_opengl_shader(SHADER_FT_REDUCE); glViewport(0, 0, 64, 36); tex[2].bind(); gl_state_active_bind_texture_2d(0, tex[1].color_texture->tex); - render_texture::draw_params(&shaders_ft, 128, 72, 0.75f, 1.0f, 1.0f, 1.0f, 1.0f); + render_texture::draw_quad(&shaders_ft, 128, 72); uniform_value[U_REDUCE] = 1; - shaders_ft.set(SHADER_FT_REDUCE); + shaders_ft.set_opengl_shader(SHADER_FT_REDUCE); glViewport(0, 0, 32, 18); tex[3].bind(); gl_state_active_bind_texture_2d(0, tex[2].color_texture->tex); - render_texture::draw_params(&shaders_ft, 64, 36, 0.75f, 1.0f, 1.0f, 1.0f, 1.0f); + render_texture::draw_quad(&shaders_ft, 64, 36); - //uniform_value[U_EXPOSURE] = 0; - //shaders_ft.set(SHADER_FT_EXPOSURE); - shaders_ft.set(SHADER_FT_EXPOSURE_MINIFY); + uniform_value[U_EXPOSURE] = 0; + shaders_ft.set_opengl_shader(SHADER_FT_EXPOSURE); glViewport(0, 0, 8, 8); tex[4].bind(); gl_state_active_bind_texture_2d(0, tex[3].color_texture->tex); - render_texture::draw_params(&shaders_ft, 32, 18); + render_texture::draw_quad(&shaders_ft, 32, 18); - vec3 intensity = data.intensity; - vec3* gauss = data.gauss; - - vec3 v[POST_PROCESS_BLUR_GAUSSIAN_KERNEL_SIZE]; - for (int32_t i = 0; i < POST_PROCESS_BLUR_GAUSSIAN_KERNEL_SIZE; i++) - v[i] = gauss[i] * intensity; - - //uniform_value[U_GAUSS] = 0; - //shaders_ft.set(SHADER_FT_GAUSS); - shaders_ft.set(SHADER_FT_GAUSS_USUAL); - shaders_ft.local_frag_set(1, 1.0f, 0.0f, 1.0f, 0.0f); - for (int32_t i = 0; i < POST_PROCESS_BLUR_GAUSSIAN_KERNEL_SIZE; i++) - shaders_ft.local_frag_set(4ULL + i, v[i].x, v[i].y, v[i].z, 0.0f); + uniform_value[U_GAUSS] = 0; + shaders_ft.set_opengl_shader(SHADER_FT_GAUSS); + gaussian_coef_ubo.Bind(1); glViewport(0, 0, 128, 72); tex[0].bind(); gl_state_active_bind_texture_2d(0, tex[1].color_texture->tex); - render_texture::draw_params(&shaders_ft, 128, 72); + render_texture::draw_quad(&shaders_ft, 128, 72, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f); fbo::blit(tex[0].fbos[0], tex[1].fbos[0], 0, 0, 128, 72, 0, 0, 128, 72, GL_COLOR_BUFFER_BIT, GL_LINEAR); @@ -131,7 +136,7 @@ void post_process_blur::get_blur(render_texture* rt) { glViewport(0, 0, 64, 36); tex[0].bind(); gl_state_active_bind_texture_2d(0, tex[2].color_texture->tex); - render_texture::draw_params(&shaders_ft, 64, 36); + render_texture::draw_quad(&shaders_ft, 64, 36, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f); fbo::blit(tex[0].fbos[0], tex[2].fbos[0], 0, 0, 64, 36, 0, 0, 64, 36, GL_COLOR_BUFFER_BIT, GL_LINEAR); @@ -139,17 +144,15 @@ void post_process_blur::get_blur(render_texture* rt) { glViewport(0, 0, 32, 18); tex[0].bind(); gl_state_active_bind_texture_2d(0, tex[3].color_texture->tex); - render_texture::draw_params(&shaders_ft, 32, 18); + render_texture::draw_quad(&shaders_ft, 32, 18); fbo::blit(tex[0].fbos[0], tex[3].fbos[0], 0, 0, 32, 18, 0, 0, 32, 18, GL_COLOR_BUFFER_BIT, GL_LINEAR); - shaders_ft.local_frag_set(1, 0.0f, 1.0f, 0.0f, 1.0f); - glViewport(0, 0, 128, 72); tex[0].bind(); gl_state_active_bind_texture_2d(0, tex[1].color_texture->tex); - render_texture::draw_params(&shaders_ft, 128, 72); + render_texture::draw_quad(&shaders_ft, 128, 72, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f); fbo::blit(tex[0].fbos[0], tex[1].fbos[0], 0, 0, 128, 72, 0, 0, 128, 72, GL_COLOR_BUFFER_BIT, GL_LINEAR); @@ -157,7 +160,7 @@ void post_process_blur::get_blur(render_texture* rt) { glViewport(0, 0, 64, 36); tex[0].bind(); gl_state_active_bind_texture_2d(0, tex[2].color_texture->tex); - render_texture::draw_params(&shaders_ft, 64, 36); + render_texture::draw_quad(&shaders_ft, 64, 36, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f); fbo::blit(tex[0].fbos[0], tex[2].fbos[0], 0, 0, 64, 36, 0, 0, 64, 36, GL_COLOR_BUFFER_BIT, GL_LINEAR); @@ -165,13 +168,13 @@ void post_process_blur::get_blur(render_texture* rt) { glViewport(0, 0, 32, 18); tex[0].bind(); gl_state_active_bind_texture_2d(0, tex[3].color_texture->tex); - render_texture::draw_params(&shaders_ft, 32, 18); + render_texture::draw_quad(&shaders_ft, 32, 18, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f); fbo::blit(tex[0].fbos[0], tex[3].fbos[0], 0, 0, 32, 18, 0, 0, 32, 18, GL_COLOR_BUFFER_BIT, GL_LINEAR); uniform_value[U_REDUCE] = 7; - shaders_ft.set(SHADER_FT_REDUCE); + shaders_ft.set_opengl_shader(SHADER_FT_REDUCE); glViewport(0, 0, 256, 144); tex[0].bind(); @@ -179,7 +182,7 @@ void post_process_blur::get_blur(render_texture* rt) { gl_state_active_bind_texture_2d(1, tex[1].color_texture->tex); gl_state_active_bind_texture_2d(2, tex[2].color_texture->tex); gl_state_active_bind_texture_2d(3, tex[3].color_texture->tex); - render_texture::draw_params(&shaders_ft, 32, 18, 0.25f, 0.15f, 0.25f, 0.25f, 0.25f); + render_texture::draw_quad(&shaders_ft, 32, 18, 0.25f, 0.15f, 0.25f, 0.25f, 0.25f); } void post_process_blur::init_fbo(int32_t width, int32_t height) { diff --git a/src/CRE/post_process/blur.hpp b/src/CRE/post_process/blur.hpp index 835ab0ce..0408e3cc 100644 --- a/src/CRE/post_process/blur.hpp +++ b/src/CRE/post_process/blur.hpp @@ -7,6 +7,7 @@ #include "../../KKdLib/default.hpp" #include "../../KKdLib/vec.hpp" +#include "../GL/uniform_buffer.hpp" #include "../shared.hpp" #include "../render_texture.hpp" @@ -27,6 +28,7 @@ struct post_process_blur { int32_t* height_down; render_texture* tex_down; int32_t count_down; + GL::UniformBuffer gaussian_coef_ubo; post_process_blur(); ~post_process_blur(); diff --git a/src/CRE/post_process/dof.cpp b/src/CRE/post_process/dof.cpp index 53bedc9e..5ff02a46 100644 --- a/src/CRE/post_process/dof.cpp +++ b/src/CRE/post_process/dof.cpp @@ -7,575 +7,16 @@ #include "../../KKdLib/str_utils.hpp" #include "../rob/rob.hpp" #include "../gl_state.hpp" +#include "../shader_ft.hpp" -struct post_process_dof_shader_data { - vec4 g_depth_params; - vec4 g_spread_scale; - vec4 g_depth_params2; +struct dof_common_shader_data { + vec4 g_depth_params; //x=(n-f)/(nf), y=1/n, z=coc_from_z_scale, w=coc_from_z_offset + vec4 g_spread_scale; //x=scale_from_meter_to_pixel, y=scale_from_meter_to_sample, + // z=scale_from_pixel_to_sample, w=scale_from_sample_to_pixel + vec4 g_depth_params2; //x=distance_to_focus_m, y=focus_range, + // z=k/(fuzzing_range*fuzzing_range), w=max_coc_radius_in_pixel //yzw=for_f2 }; -static const char* dof_vert_shader = -"#version 430\n" -"out VertexData {\n" -" vec2 texcoord;\n" -"} result;\n" -"\n" -"void main() {\n" -" gl_Position.x = -1.0 + float(gl_VertexID / 2) * 4.0;\n" -" gl_Position.y = 1.0 - float(gl_VertexID % 2) * 4.0;\n" -" gl_Position.z = 0.0;\n" -" gl_Position.w = 1.0;\n" -" result.texcoord = gl_Position.xy * 0.5 + 0.5;\n" -"}\n"; - -static const char* dof_frag_shader_version = "#version 430 core\n"; - -static const char* dof_frag_shader_render_tile_part_2 = -"#version 430 core\n" -"layout(binding = 0) uniform sampler2D g_tile; //r=min_depth_m, g=max_coc_pixel\n" -"\n" -"layout(location = 0) out vec2 result; //r=min_depth_m, g=max_coc_pixel\n" -"\n" -"in VertexData {\n" -" vec2 texcoord;\n" -"} frg;\n" -"\n" -"void main() {\n" -" //3x3 sample\n" -" vec2 texture_size = textureSize(g_tile, 0);\n" -" vec2 texel_size = vec2(1.0) / texture_size;\n" -" vec2 uv = frg.texcoord;\n" -" const vec2 v00 = texture(g_tile, uv + vec2(-texel_size.x, -texel_size.y)).rg;\n" -" const vec2 v01 = texture(g_tile, uv + vec2( 0.0, -texel_size.y)).rg;\n" -" const vec2 v02 = texture(g_tile, uv + vec2( texel_size.x, -texel_size.y)).rg;\n" -" const vec2 v10 = texture(g_tile, uv + vec2(-texel_size.x, 0.0)).rg;\n" -" const vec2 v11 = texture(g_tile, uv + vec2( 0.0, 0.0)).rg;\n" -" const vec2 v12 = texture(g_tile, uv + vec2( texel_size.x, 0.0)).rg;\n" -" const vec2 v20 = texture(g_tile, uv + vec2(-texel_size.x, texel_size.y)).rg;\n" -" const vec2 v21 = texture(g_tile, uv + vec2( 0.0, texel_size.y)).rg;\n" -" const vec2 v22 = texture(g_tile, uv + vec2( texel_size.x, texel_size.y)).rg;\n" -"\n" -" //depth min\n" -" result.r = min(v00.r, v01.r);\n" -" result.r = min(result.r, v02.r);\n" -" result.r = min(result.r, v10.r);\n" -" result.r = min(result.r, v11.r);\n" -" result.r = min(result.r, v12.r);\n" -" result.r = min(result.r, v20.r);\n" -" result.r = min(result.r, v21.r);\n" -" result.r = min(result.r, v22.r);\n" -" //CoC max\n" -" result.g = max(v00.g, v01.g);\n" -" result.g = max(result.g, v02.g);\n" -" result.g = max(result.g, v10.g);\n" -" result.g = max(result.g, v11.g);\n" -" result.g = max(result.g, v12.g);\n" -" result.g = max(result.g, v20.g);\n" -" result.g = max(result.g, v21.g);\n" -" result.g = max(result.g, v22.g);\n" -"}\n"; - -static const char* dof_frag_shader_f2_define = "#define USE_F2_COC (1)\n\n"; -static const char* dof_frag_shader_physical_define = "#define USE_F2_COC (0)\n\n"; - -static const char* dof_frag_shader_shared = -"#define SCALE_FROM_METER_TO_PIXEL g_spread_scale.x\n" -"#define SCALE_FROM_METER_TO_SAMPLE g_spread_scale.y\n" -"#define SCALE_FROM_PIXEL_TO_SAMPLE g_spread_scale.z\n" -"#define SCALE_FROM_SAMPLE_TO_PIXEL g_spread_scale.w\n" -"#define SAMPLE_DIVISION (7)\n" -"#define SAMPLE_COUNT (SAMPLE_DIVISION * SAMPLE_DIVISION)\n" -"#define PI 3.1415926535897932\n" -"#define DOF_SINGLE_PIXEL_RADIUS 0.7071067811865475\n" -"#define DOF_DEPTH_SCALE_FOREGROUND 0.0 //Give up on front bokeh at 0 //0.05f //Smaller, more foreground\n" -"#define MAX_COC_RADIUS_PIXEL (8)\n" -"\n" -"layout(binding = 0) uniform Common {\n" -" uniform vec4 g_depth_params; //x=(n-f)/(nf), y=1/n, z=coc_from_z_scale, w=coc_from_z_offset\n" -" uniform vec4 g_spread_scale; //x=scale_from_meter_to_pixel, y=scale_from_meter_to_sample," -" z=scale_from_pixel_to_sample, w=scale_from_sample_to_pixel\n" -" uniform vec4 g_depth_params2; //x=distance_to_focus_m, y=focus_range," -" z=k/(fuzzing_range*fuzzing_range), w=max_coc_radius_in_pixel //yzw=for_f2\n" -"};\n" -"\n" -"float calculate_depth_m_from_value_in_zbuffer(const float value_in_zbuffer) {\n" -" return 1.0 / (value_in_zbuffer * g_depth_params.x + g_depth_params.y); //Linearization\n" -"}\n" -"\n" -"vec4 calculate_depth_m_from_value_in_zbuffer(const vec4 value_in_zbuffer) {\n" -" return vec4(1.0) / (value_in_zbuffer * g_depth_params.x + g_depth_params.y);\n" -"}\n" -"\n" -"float clamp_coc_pixel(const float radius_pixel) {\n" -" return min(radius_pixel, MAX_COC_RADIUS_PIXEL);\n" -"}\n" -"\n" -"#if USE_F2_COC\n" -"float calculate_coc_pixel_from_depth(const float linear_depth) {\n" -" float depth_dist = max(abs(g_depth_params2.x - linear_depth) - g_depth_params2.y, 0.0);\n" -" float dof_ratio = 1.0 - exp(depth_dist * depth_dist * g_depth_params2.z);\n" -" return dof_ratio * g_depth_params2.w;\n" -"}\n" -"\n" -"float calculate_coc_pixel_from_value_in_zbuffer(const float value_in_zbuffer) {\n" -" float d = calculate_depth_m_from_value_in_zbuffer(value_in_zbuffer);\n" -" return calculate_coc_pixel_from_depth(d);\n" -"}\n" -"#else\n" -"const float TEST_SCALE = 10.0;\n" -"float calculate_coc_pixel_from_value_in_zbuffer(const float value_in_zbuffer) {\n" -" return clamp_coc_pixel(abs(value_in_zbuffer * g_depth_params.z" - " + g_depth_params.w) * 1000.0 * TEST_SCALE);" -" //Appropriate; converted to 1[m]=100[pixel]. @todo Appropriate\n" -"}\n" -"\n" -"float calculate_coc_pixel_from_depth(const float linear_depth) {\n" -" float z = 1.0 / ((linear_depth - g_depth_params.y) * g_depth_params.x);" -" //It's possible to divide by zero. @todo Summarize calculations in advance\n" -" return calculate_coc_pixel_from_value_in_zbuffer(z);\n" -"}\n" -"#endif\n" -"\n" -"float calculate_sample_alpha(const float sample_coc_in_pixel) {\n" -" const float t = DOF_SINGLE_PIXEL_RADIUS * DOF_SINGLE_PIXEL_RADIUS;\n" -" float coc = sample_coc_in_pixel * sample_coc_in_pixel;\n" -" return 1.0 / (PI * max(t, coc)); //Reciprocal of area of CoC." - " Energy is conserved by dividing by area\n" -"}\n" -"\n" -"float calculate_background_depth_weight(const float depth, const float tile_min_depth) {" -" //1 for background. 0 for foreground\n" -" float d = DOF_DEPTH_SCALE_FOREGROUND * (depth - tile_min_depth);\n" -" return smoothstep(0.0, 1.0, d);\n" -"}\n" -"\n"; - -static const char* dof_frag_shader_render_tile_part_1 = -"layout(binding = 0) uniform sampler2D g_depth;\n" -"\n" -"layout(location = 0) out vec4 result; //r=min_depth_m, g=max_coc_pixel\n" -"\n" -"in VertexData {\n" -" vec2 texcoord;\n" -"} frg;\n" -"\n" -"#if 0 //Original\n" -"void main() {\n" -" //Tiling. r=min depth, g=max CoC\n" -"#define N (20)\n" -" ivec2 texture_size = textureSize(g_depth, 0);\n" -" const vec2 step = 1.0 / vec2(texture_size);\n" -" vec2 lt_uv = frg.texcoord + (-N * 0.5 + 0.5) * step;\n" -" vec2 uv = lt_uv;\n" -" float min_z = 1.0;\n" -" float max_coc = 0.0;\n" -" for(int i = 0; i < N; i++) {\n" -" uv.x = lt_uv.x;\n" -" for(int j = 0; j < N; j++) { //@todo Try reducing number of loops with textureGatherOffsets\n" -" float z = texture(g_depth, uv).r;\n" -" min_z = min(min_z, z);\n" -" float coc = calculate_coc_pixel_from_value_in_zbuffer(z);\n" -" max_coc = max(max_coc, coc);\n" -" uv.x += step.x;\n" -" }\n" -" uv.y += step.y;\n" -" }\n" -" result.r = calculate_depth_m_from_value_in_zbuffer(min_z); //Linearization\n" -" result.g = max_coc;\n" -" //result.g = 8.0<=max_coc?1.0:0.0;\n" -"}\n" -"#else //Optimized\n" -"vec4 clamp_coc_pixel(const vec4 radius_pixel) {\n" -" return min(radius_pixel, MAX_COC_RADIUS_PIXEL);\n" -"}\n" -"\n" -"#if USE_F2_COC\n" -"vec4 calculate_coc_pixel_from_depth(const vec4 linear_depth) {\n" -" vec4 depth_dist = max(abs(g_depth_params2.x - linear_depth) - g_depth_params2.y, 0.0);\n" -" vec4 dof_ratio = 1.0 - exp(depth_dist * depth_dist * g_depth_params2.z);\n" -" return dof_ratio * g_depth_params2.w;\n" -"}\n" -"\n" -"vec4 calculate_coc_pixel_from_value_in_zbuffer(const vec4 value_in_zbuffer) {\n" -" vec4 d = calculate_depth_m_from_value_in_zbuffer(value_in_zbuffer);\n" -" return calculate_coc_pixel_from_depth(d);\n" -"}\n" -"#else\n" -"vec4 calculate_coc_pixel_from_value_in_zbuffer(const vec4 value_in_zbuffer) {\n" -" return clamp_coc_pixel(abs(value_in_zbuffer * g_depth_params.z" -" + g_depth_params.w) * 1000.0 * TEST_SCALE);\n" -" //Appropriate; converted to 1[m]=100[pixel]. @todo Appropriate\n" -"}\n" -"#endif\n" -"\n" -"void main() {\n" -" //Tiling. r=min depth, g=max CoC\n" -"#define N (10)\n" -" ivec2 texture_size = textureSize(g_depth, 0);\n" -" const vec2 step = 1.0 / vec2(texture_size) * 2.0;\n" -" vec2 lt_uv = frg.texcoord + (-N * 0.5 + 0.25) * step;\n" -" vec2 uv = lt_uv;\n" -" float min_z = 1.0;\n" -" float max_coc = 0.0;\n" -" for(int i = 0; i < N; i++) {\n" -" uv.x = lt_uv.x;\n" -" for(int j = 0; j < N; j++) {\n" -" vec4 z = textureGather(g_depth, uv, 0);\n" -" min_z = min(min_z, z.x);\n" -" min_z = min(min_z, z.y);\n" -" min_z = min(min_z, z.z);\n" -" min_z = min(min_z, z.w);\n" -" vec4 coc = calculate_coc_pixel_from_value_in_zbuffer(z);\n" -" max_coc = max(max_coc, coc.x);\n" -" max_coc = max(max_coc, coc.y);\n" -" max_coc = max(max_coc, coc.z);\n" -" max_coc = max(max_coc, coc.w);\n" -" uv.x += step.x;\n" -" }\n" -" uv.y += step.y;\n" -" }\n" -" result.r = calculate_depth_m_from_value_in_zbuffer(min_z); //Linearization\n" -" result.g = max_coc;\n" -" //result.g = 8.0<=max_coc?1.0:0.0;\n" -"}\n" -"#endif\n"; - -static const char* dof_frag_shader_downsample = -"layout(binding = 0) uniform sampler2D g_depth_point_sampler;\n" -"layout(binding = 1) uniform sampler2D g_color_linear_sampler;\n" -"layout(binding = 2) uniform sampler2D g_tile_sampler;\n" -"\n" -"layout(location = 0) out vec3 result_prefilter;\n" -"layout(location = 1) out vec3 result_presort;\n" -"\n" -"in VertexData {\n" -" vec2 texcoord;\n" -"} frg;\n" -"\n" -"float fetch_max_depth(in const vec2 texcoord) {\n" -" vec4 z = textureGather(g_depth_point_sampler, texcoord, 0); //4 samples with bilinear footprint\n" -" return max(max(z.x, z.y), max(z.z, z.w)); //Select farthest depth\n" -"}\n" -"\n" -"float calculate_min_bilateral_weight(\n" -" in const vec2 texcoord,\n" -" in const float depth_center,\n" -" in const float side_center) { //If depth_center is foreground, use only foreground samples." -" If depth_center is background, use only background samples\n" -" const float depth_scale = 1.0;\n" -"#if 1\n" -" vec4 z = calculate_depth_m_from_value_in_zbuffer(textureGather(g_depth_point_sampler, texcoord, 0));" -" //It might be better to keep it non-linear and choose one with largest difference\n" -" z -= vec4(depth_center);\n" -" z = abs(z);\n" -" float d = max(max(z.x, z.y), max(z.z, z.w)); //Select farthest depth\n" -" return 1.0 / (d * depth_scale + 1.0);\n" -"#elif 0//Think!\n" -" vec4 z = textureGather(g_depth_point_sampler, texcoord, 0);\n" -" float max_z = calculate_depth_m_from_value_in_zbuffer(max(max(z.x, z.y), max(z.z, z.w)));" - " //Select farthest depth\n" -" if (0.0 < side_center * (max_z - g_depth_params2.x)) {" - " //On same side (foreground, background) as center tap\n" -" z = calculate_depth_m_from_value_in_zbuffer(z);\n" -" z -= vec4(depth_center);\n" -" z = abs(z);\n" -" float d = max(max(z.x, z.y), max(z.z, z.w)); //Select farthest depth\n" -" return 1.0 / (d * depth_scale + 1.0);\n" -" }\n" -" else\n" -" return 0.0;\n" -"#else//Think!\n" -" vec4 z = calculate_depth_m_from_value_in_zbuffer(textureGather(g_depth_point_sampler, texcoord, 0));\n" -" vec4 d = z - vec4(depth_center);\n" -" d = abs(d); //Choose largest component\n" -" float rz, rd;\n" -" if (d.x < d.y) { //y, z, w\n" -" if (d.y < d.z) { //z, w\n" -" if (d.z < d.w) { //w\n" -" rz = z.w;\n" -" rd = d.w;\n" -" }\n" -" else { //z\n" -" rz = z.z;\n" -" rd = d.z;\n" -" }\n" -" }\n" -" else if (d.y < d.w) { //w\n" -" rz = z.w;\n" -" rd = d.w;\n" -" }\n" -" else { //y\n" -" rz = z.y;\n" -" rd = d.y;\n" -" }\n" -" }\n" -" else if (d.x < d.z) { //z, w\n" -" if (d.z < d.w) { //w\n" -" rz = z.w;\n" -" rd = d.w;\n" -" }\n" -" else { //z\n" -" rz = z.z;\n" -" rd = d.z;\n" -" }\n" -" }\n" -" else if (d.x < d.w) { //w\n" -" rz = z.w;\n" -" rd = d.w;\n" -" }\n" -" else { //x\n" -" rz = z.x;\n" -" rd = d.x;\n" -" }\n" -" return (0.0 <= (rz - g_depth_params2.x) * side_center) ? (1.0 / (rd * depth_scale + 1.0)) : 0.0;" -" //Weight 0 if center tap and side (foreground, background) are different\n" -"#endif\n" -"}\n" -"float luminace_from_rgb(in const vec3 rgb){\n" -" return dot(vec3(0.3, 0.59, 0.11), rgb);\n" -"}\n" -"\n" -"vec3 calculate_karis_average(in const vec3 color, const float sharpness){\n" -" return color * (1.0 / (1.0 + (1.0 - sharpness) * luminace_from_rgb(color)));\n" -"}\n" -"\n" -"//9 points are sampling and combining with bilateral filter using depth as a weight\n" -"vec3 prefilter(float coc_center_pixel, float depth_center) {\n" -" const float side_center = depth_center - g_depth_params2.x;" - " //foreground->side_center<0, background->0is_visible()) continue; - mat4 mat; - sub_1405163C0(rob_chr, 4, &mat); + mat4 mat = rob_chr->data.field_1E68.field_78[4]; // sub_1405163C0 vec3 chara_trans = 0.0f; mat4_get_translation(&mat, &chara_trans); @@ -740,22 +169,11 @@ void post_process_dof::init_fbo(int32_t width, int32_t height) { glTexStorage2D(GL_TEXTURE_2D, 1, GL_R8, w2, h2); fbo[3].init_data(w2, h2, &textures[4], 2, 0); - glGenBuffers(2, ubo); - gl_state_bind_uniform_buffer(ubo[0], true); - if (GLAD_GL_VERSION_4_4) - glBufferStorage(GL_UNIFORM_BUFFER, - sizeof(post_process_dof_shader_data), 0, GL_DYNAMIC_STORAGE_BIT); - else - glBufferData(GL_UNIFORM_BUFFER, - sizeof(post_process_dof_shader_data), 0, GL_DYNAMIC_DRAW); - - vec2 data[49] = {}; + vec2 data[50] = {}; post_process_dof_calculate_texcoords(data, 3.0f); - gl_state_bind_uniform_buffer(ubo[1], true); - if (GLAD_GL_VERSION_4_4) - glBufferStorage(GL_UNIFORM_BUFFER, sizeof(data), data, 0); - else - glBufferData(GL_UNIFORM_BUFFER, sizeof(data), data, GL_STATIC_DRAW); + common_ubo.Create(sizeof(dof_common_shader_data)); + texcoords_ubo.Create(sizeof(data), data); + glGenVertexArrays(1, &vao); } @@ -839,10 +257,8 @@ static void post_process_dof_free_fbo(post_process_dof* dof) { dof->vao = 0; } - if (dof->ubo[0]) { - glDeleteBuffers(2, dof->ubo); - dof->ubo[0] = 0; - } + dof->texcoords_ubo.Destroy(); + dof->common_ubo.Destroy(); if (dof->textures[0]) { glDeleteTextures(6, dof->textures); @@ -850,96 +266,6 @@ static void post_process_dof_free_fbo(post_process_dof* dof) { } } -static void post_process_dof_load_shaders(post_process_dof* dof) { - char* frag_shader_string[9]; - frag_shader_string[0] = str_utils_copy(dof_frag_shader_render_tile_part_2); - for (int32_t i = 0; i < 2; i++) { - char* t0; - char* t1; - const char* define = i ? dof_frag_shader_f2_define : dof_frag_shader_physical_define; - char* shared = str_utils_add(define, dof_frag_shader_shared); - - t0 = str_utils_copy(dof_frag_shader_version); - t1 = str_utils_add(t0, shared); - frag_shader_string[1 + i * 4] = str_utils_add(t1, dof_frag_shader_render_tile_part_1); - free_def(t0); - free_def(t1); - - t0 = str_utils_copy(dof_frag_shader_version); - t1 = str_utils_add(t0, shared); - frag_shader_string[2 + i * 4] = str_utils_add(t1, dof_frag_shader_downsample); - free_def(t0); - free_def(t1); - - t0 = str_utils_copy(dof_frag_shader_version); - t1 = str_utils_add(t0, shared); - frag_shader_string[3 + i * 4] = str_utils_add(t1, dof_frag_shader_apply_main_filter); - free_def(t0); - free_def(t1); - - t0 = str_utils_copy(dof_frag_shader_version); - t1 = str_utils_add(t0, shared); - frag_shader_string[4 + i * 4] = str_utils_add(t1, dof_frag_shader_upsample); - free_def(t0); - free_def(t1); - free_def(shared); - } - - GLuint vert_shader = post_process_dof_shader_compile(GL_VERTEX_SHADER, dof_vert_shader); - for (int32_t i = 0; i < 9; i++) { - GLuint frag_shader = post_process_dof_shader_compile(GL_FRAGMENT_SHADER, frag_shader_string[i]); - dof->program[i] = post_process_dof_program_link(vert_shader, frag_shader); - glDeleteShader(frag_shader); - free_def(frag_shader_string[i]); - } - glDeleteShader(vert_shader); -} - -static GLuint post_process_dof_shader_compile(GLenum type, const char* data) { - if (!data) - return 0; - - GLuint shader = glCreateShader(type); - glShaderSource(shader, 1, (const GLchar* const*)&data, 0); - glCompileShader(shader); - - GLint success = 0; - glGetShaderiv(shader, GL_COMPILE_STATUS, &success); - if (!success) { - GLchar* info_log = force_malloc_s(GLchar, 0x10000); - glGetShaderInfoLog(shader, 0x10000, 0, info_log); - printf_debug("DOF Shader compile error:\n%s\n", info_log); - free_def(info_log); - } - return shader; -} - -static GLuint post_process_dof_program_link(GLuint vert_shad, GLuint frag_shad) { - GLuint program = glCreateProgram(); - if (vert_shad) - glAttachShader(program, vert_shad); - if (frag_shad) - glAttachShader(program, frag_shad); - glLinkProgram(program); - - GLint success = 0; - glGetProgramiv(program, GL_LINK_STATUS, &success); - if (!success) { - GLchar* info_log = force_malloc_s(GLchar, 0x10000); - glGetProgramInfoLog(program, 0x10000, 0, info_log); - printf_debug("DOF shader linking error:\n%s\n", info_log); - free_def(info_log); - glDeleteProgram(program); - return 0; - } - return program; -}; - -static void sub_1405163C0(rob_chara* rob_chr, int32_t a2, mat4* mat) { - if (a2 >= 0 && a2 <= 26) - *mat = rob_chr->data.field_1E68.field_78[a2]; -} - namespace renderer { void DOF3::apply_f2(post_process_dof* dof, render_texture* rt, render_texture* buf, GLuint* samplers, GLuint color_texture, @@ -951,13 +277,15 @@ namespace renderer { update_data(dof, min_distance, max_distance, fov, focus, 0.0f, 1.0f, focus_range, fuzzing_range, ratio); + uniform_value[U_DOF] = 1; + gl_state_bind_vertex_array(dof->vao); render_tile(dof, samplers, depth_texture, true); downsample(dof, samplers, color_texture, depth_texture, true); - apply_main_filter(dof, samplers, true); + main_filter(dof, samplers, true); upsample(dof, rt, buf, samplers, color_texture, depth_texture, true); - gl_state_use_program(0); + shader_opengl::unbind(); for (int32_t i = 0; i < 8; i++) { gl_state_bind_sampler(i, 0); gl_state_active_bind_texture_2d(i, 0); @@ -975,13 +303,15 @@ namespace renderer { update_data(dof, min_distance, max_distance, fov, focus, focal_length, f_number, 0.0f, 0.1f, 0.0f); + uniform_value[U_DOF] = 0; + gl_state_bind_vertex_array(dof->vao); render_tile(dof, samplers, depth_texture, false); downsample(dof, samplers, color_texture, depth_texture, false); - apply_main_filter(dof, samplers, false); + main_filter(dof, samplers, false); upsample(dof, rt, buf, samplers, color_texture, depth_texture, false); - gl_state_use_program(0); + shader_opengl::unbind(); for (int32_t i = 0; i < 8; i++) { gl_state_bind_sampler(i, 0); gl_state_active_bind_texture_2d(i, 0); @@ -993,21 +323,20 @@ namespace renderer { GLuint* samplers, GLuint depth_texture, bool f2) { gl_state_bind_framebuffer(dof->fbo[0].buffer); glViewport(0, 0, dof->fbo[0].width, dof->fbo[0].height); - if (f2) - gl_state_use_program(dof->program[5]); - else - gl_state_use_program(dof->program[1]); - gl_state_bind_uniform_buffer_base(0, dof->ubo[0]); + uniform_value[U_DOF_STAGE] = 0; + shaders_ft.set_opengl_shader(SHADER_FT_DOF); + dof->common_ubo.Bind(0); gl_state_active_bind_texture_2d(0, depth_texture); gl_state_bind_sampler(0, samplers[1]); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); gl_state_bind_framebuffer(dof->fbo[1].buffer); glViewport(0, 0, dof->fbo[1].width, dof->fbo[1].height); - gl_state_use_program(dof->program[0]); + uniform_value[U_DOF_STAGE] = 1; + shaders_ft.set_opengl_shader(SHADER_FT_DOF); gl_state_active_bind_texture_2d(0, dof->textures[0]); gl_state_bind_sampler(0, samplers[1]); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } @@ -1015,48 +344,41 @@ namespace renderer { GLuint* samplers, GLuint color_texture, GLuint depth_texture, bool f2) { gl_state_bind_framebuffer(dof->fbo[2].buffer); glViewport(0, 0, dof->fbo[2].width, dof->fbo[2].height); - if (f2) - gl_state_use_program(dof->program[6]); - else - gl_state_use_program(dof->program[2]); - gl_state_bind_uniform_buffer_base(0, dof->ubo[0]); + uniform_value[U_DOF_STAGE] = 2; + shaders_ft.set_opengl_shader(SHADER_FT_DOF); + dof->common_ubo.Bind(0); gl_state_active_bind_texture_2d(0, depth_texture); gl_state_bind_sampler(0, samplers[1]); gl_state_active_bind_texture_2d(1, color_texture); gl_state_bind_sampler(1, samplers[0]); gl_state_active_bind_texture_2d(2, dof->textures[1]); gl_state_bind_sampler(2, samplers[1]); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } - void DOF3::apply_main_filter(post_process_dof* dof, GLuint* samplers, bool f2) { + void DOF3::main_filter(post_process_dof* dof, GLuint* samplers, bool f2) { gl_state_bind_framebuffer(dof->fbo[3].buffer); glViewport(0, 0, dof->fbo[3].width, dof->fbo[3].height); - if (f2) - gl_state_use_program(dof->program[7]); - else - gl_state_use_program(dof->program[3]); - gl_state_bind_uniform_buffer_base(0, dof->ubo[0]); - gl_state_bind_uniform_buffer_base(1, dof->ubo[1]); + uniform_value[U_DOF_STAGE] = 3; + shaders_ft.set_opengl_shader(SHADER_FT_DOF); + dof->common_ubo.Bind(0); + dof->texcoords_ubo.Bind(1); gl_state_active_bind_texture_2d(0, dof->textures[3]); gl_state_bind_sampler(0, samplers[1]); gl_state_active_bind_texture_2d(1, dof->textures[2]); gl_state_bind_sampler(1, samplers[1]); gl_state_active_bind_texture_2d(2, dof->textures[1]); gl_state_bind_sampler(2, samplers[1]); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } void DOF3::upsample(post_process_dof* dof, render_texture* rt, render_texture* buf, GLuint* samplers, GLuint color_texture, GLuint depth_texture, bool f2) { buf->bind(); glViewport(0, 0, dof->width, dof->height); - if (f2) - gl_state_use_program(dof->program[8]); - else - gl_state_use_program(dof->program[4]); - gl_state_bind_uniform_buffer_base(0, dof->ubo[0]); - gl_state_bind_uniform_buffer_base(1, dof->ubo[1]); + uniform_value[U_DOF_STAGE] = 4; + shaders_ft.set_opengl_shader(SHADER_FT_DOF); + dof->common_ubo.Bind(0); gl_state_active_bind_texture_2d(0, dof->textures[4]); gl_state_bind_sampler(0, samplers[1]); gl_state_active_bind_texture_2d(1, dof->textures[5]); @@ -1067,7 +389,7 @@ namespace renderer { gl_state_bind_sampler(3, samplers[1]); gl_state_active_bind_texture_2d(4, depth_texture); gl_state_bind_sampler(4, samplers[1]); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); fbo::blit(buf->fbos[0], rt->fbos[0], 0, 0, buf->color_texture->width, buf->color_texture->height, @@ -1082,25 +404,19 @@ namespace renderer { fl = dist + 0.1f; fl = fl / (dist - fl) * fl / f_number; - post_process_dof_shader_data data; - data.g_depth_params.x = 1.0f / (min_dist * max_dist) * (min_dist - max_dist); - data.g_depth_params.y = 1.0f / min_dist; - data.g_depth_params.z = -((fl * dist * (min_dist - max_dist)) * (1.0f / (min_dist * max_dist))); - data.g_depth_params.w = (1.0f - 1.0f / min_dist * dist) * fl; - data.g_spread_scale.x = 720.0f / (tanf(fov * 0.5f) * (min_dist * 2.0f)); - data.g_spread_scale.y = data.g_spread_scale.x * (float_t)(1.0 / 3.0); - data.g_spread_scale.z = (float_t)(1.0 / 3.0); - data.g_spread_scale.w = 3.0f; - data.g_depth_params2.x = dist; - data.g_depth_params2.y = focus_range; - data.g_depth_params2.z = -4.5f / (fuzzing_range * fuzzing_range); - data.g_depth_params2.w = ratio * 8.0f; - - if (GLAD_GL_VERSION_4_5) - glNamedBufferSubData(dof->ubo[0], 0, sizeof(post_process_dof_shader_data), &data); - else { - gl_state_bind_uniform_buffer(dof->ubo[0]); - glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(post_process_dof_shader_data), &data); - } + dof_common_shader_data shader_data = {}; + shader_data.g_depth_params.x = 1.0f / (min_dist * max_dist) * (min_dist - max_dist); + shader_data.g_depth_params.y = 1.0f / min_dist; + shader_data.g_depth_params.z = -((fl * dist * (min_dist - max_dist)) * (1.0f / (min_dist * max_dist))); + shader_data.g_depth_params.w = (1.0f - 1.0f / min_dist * dist) * fl; + shader_data.g_spread_scale.x = 720.0f / (tanf(fov * 0.5f) * (min_dist * 2.0f)); + shader_data.g_spread_scale.y = shader_data.g_spread_scale.x * (float_t)(1.0 / 3.0); + shader_data.g_spread_scale.z = (float_t)(1.0 / 3.0); + shader_data.g_spread_scale.w = 3.0f; + shader_data.g_depth_params2.x = dist; + shader_data.g_depth_params2.y = focus_range; + shader_data.g_depth_params2.z = -4.5f / (fuzzing_range * fuzzing_range); + shader_data.g_depth_params2.w = ratio * 8.0f; + dof->common_ubo.WriteMapMemory(shader_data); } } diff --git a/src/CRE/post_process/dof.hpp b/src/CRE/post_process/dof.hpp index b95c3551..e6fd05b2 100644 --- a/src/CRE/post_process/dof.hpp +++ b/src/CRE/post_process/dof.hpp @@ -7,6 +7,7 @@ #include "../../KKdLib/default.hpp" #include "../../KKdLib/vec.hpp" +#include "../GL/uniform_buffer.hpp" #include "../camera.hpp" #include "../fbo.hpp" #include "../render_texture.hpp" @@ -51,8 +52,8 @@ struct post_process_dof { GLuint textures[6]; fbo fbo[4]; GLuint vao; - GLuint program[9]; - GLuint ubo[2]; + GL::UniformBuffer common_ubo; + GL::UniformBuffer texcoords_ubo; post_process_dof(); ~post_process_dof(); @@ -62,7 +63,7 @@ struct post_process_dof { void initialize_data(dof_debug* debug, dof_pv* pv); void get_dof_debug(dof_debug* debug); - void set_dof_debug(dof_debug* debug); + void set_dof_debug(dof_debug* debug = 0); void get_dof_pv(dof_pv* pv); - void set_dof_pv(dof_pv* pv); + void set_dof_pv(dof_pv* pv = 0); }; diff --git a/src/CRE/post_process/exposure.cpp b/src/CRE/post_process/exposure.cpp index ea4d6a1a..3926c83e 100644 --- a/src/CRE/post_process/exposure.cpp +++ b/src/CRE/post_process/exposure.cpp @@ -10,9 +10,14 @@ #include "../post_process.hpp" #include "../shader_ft.hpp" +struct exposure_measure_shader_data { + vec4 g_spot_weight; + vec4 g_spot_coefficients[32]; +}; + static void sub_1405163C0(rob_chara* rob_chr, int32_t a2, mat4* mat); -post_process_exposure_chara_data::post_process_exposure_chara_data() : field_80(), query(), query_data() { +post_process_exposure_chara_data::post_process_exposure_chara_data() : spot_weight(), query(), query_data() { for (GLuint& i : query_data) i = -1; } @@ -20,12 +25,15 @@ post_process_exposure_chara_data::post_process_exposure_chara_data() : field_80( post_process_exposure::post_process_exposure() : exposure_history_counter(), chara_data(), query_index() { chara_data = new post_process_exposure_chara_data[ROB_CHARA_COUNT]; + exposure_measure_ubo.Create(sizeof(exposure_measure_shader_data)); } post_process_exposure::~post_process_exposure() { if (!this) return; + exposure_measure_ubo.Destroy(); + for (int32_t i = 0; i < ROB_CHARA_COUNT; i++) glDeleteQueries(3, chara_data[i].query); @@ -37,8 +45,9 @@ void post_process_exposure::get_exposure(camera* cam, int32_t render_width, bool v2 = false; if (!reset_exposure) { for (int32_t i = 0; i < ROB_CHARA_COUNT; i++) { - memset(&chara_data[i].field_0, 0, sizeof(chara_data[i].field_0)); - chara_data[i].field_80 = 0.0f; + memset(&chara_data[i].spot_coefficients, + 0, sizeof(chara_data[i].spot_coefficients)); + chara_data[i].spot_weight = 0.0f; } post_process_exposure_chara_data* v6 = chara_data; @@ -107,77 +116,81 @@ void post_process_exposure::get_exposure(camera* cam, int32_t render_width, else v22 = 1.0f; - v6->field_0[0].x = (v13 + v21 * 0.0f * v18 + 1.0f) * 0.5f; - v6->field_0[0].y = (v14 + v21 * 0.1f + 1.0f) * 0.5f; - v6->field_0[0].z = 0.0f; - v6->field_0[0].w = 4.0f; - v6->field_0[1].x = (v13 + v21 * 0.0f * v18 + 1.0f) * 0.5f; - v6->field_0[1].y = (v14 - v21 * 0.3f + 1.0f) * 0.5f; - v6->field_0[1].z = 0.0f; - v6->field_0[1].w = 4.0f; - v6->field_0[2].x = (v13 + v21 * -0.5f * v18 + 1.0f) * 0.5f; - v6->field_0[2].y = (v14 + v21 * -0.5f + 1.0f) * 0.5f; - v6->field_0[2].z = 0.0f; - v6->field_0[2].w = 3.0f; - v6->field_0[3].x = (v13 - v21 * 0.6f * v18 + 1.0f) * 0.5f; - v6->field_0[3].y = (v14 - v21 * 0.1f + 1.0f) * 0.5f; - v6->field_0[3].z = 0.0f; - v6->field_0[3].w = 2.0f; - v6->field_0[4].x = (v13 + v21 * 0.6f * v18 + 1.0f) * 0.5f; - v6->field_0[4].y = (v14 - v21 * 0.1f + 1.0f) * 0.5f; - v6->field_0[4].z = 0.0f; - v6->field_0[4].w = 2.0f; - v6->field_0[5].x = (v13 + v21 * 0.5f * v18 + 1.0f) * 0.5f; - v6->field_0[5].y = (v14 + v21 * -0.5f + 1.0f) * 0.5f; - v6->field_0[5].z = 0.0f; - v6->field_0[5].w = 3.0f; - v6->field_0[6].x = (v13 + v21 * 0.0f * v18 + 1.0f) * 0.5f; - v6->field_0[6].y = (v14 - v21 * 0.8f + 1.0f) * 0.5f; - v6->field_0[6].z = 0.0f; - v6->field_0[6].w = 3.0f; - v6->field_80 = v16 * 1.6f * v17 * v22; - if (v6->field_80 > 0.0f && !v6->query_data[query_index]) + v6->spot_coefficients[0].x = (v13 + v21 * 0.0f * v18 + 1.0f) * 0.5f; + v6->spot_coefficients[0].y = (v14 + v21 * 0.1f + 1.0f) * 0.5f; + v6->spot_coefficients[0].z = 0.0f; + v6->spot_coefficients[0].w = 4.0f; + v6->spot_coefficients[1].x = (v13 + v21 * 0.0f * v18 + 1.0f) * 0.5f; + v6->spot_coefficients[1].y = (v14 - v21 * 0.3f + 1.0f) * 0.5f; + v6->spot_coefficients[1].z = 0.0f; + v6->spot_coefficients[1].w = 4.0f; + v6->spot_coefficients[2].x = (v13 + v21 * -0.5f * v18 + 1.0f) * 0.5f; + v6->spot_coefficients[2].y = (v14 + v21 * -0.5f + 1.0f) * 0.5f; + v6->spot_coefficients[2].z = 0.0f; + v6->spot_coefficients[2].w = 3.0f; + v6->spot_coefficients[3].x = (v13 - v21 * 0.6f * v18 + 1.0f) * 0.5f; + v6->spot_coefficients[3].y = (v14 - v21 * 0.1f + 1.0f) * 0.5f; + v6->spot_coefficients[3].z = 0.0f; + v6->spot_coefficients[3].w = 2.0f; + v6->spot_coefficients[4].x = (v13 + v21 * 0.6f * v18 + 1.0f) * 0.5f; + v6->spot_coefficients[4].y = (v14 - v21 * 0.1f + 1.0f) * 0.5f; + v6->spot_coefficients[4].z = 0.0f; + v6->spot_coefficients[4].w = 2.0f; + v6->spot_coefficients[5].x = (v13 + v21 * 0.5f * v18 + 1.0f) * 0.5f; + v6->spot_coefficients[5].y = (v14 + v21 * -0.5f + 1.0f) * 0.5f; + v6->spot_coefficients[5].z = 0.0f; + v6->spot_coefficients[5].w = 3.0f; + v6->spot_coefficients[6].x = (v13 + v21 * 0.0f * v18 + 1.0f) * 0.5f; + v6->spot_coefficients[6].y = (v14 - v21 * 0.8f + 1.0f) * 0.5f; + v6->spot_coefficients[6].z = 0.0f; + v6->spot_coefficients[6].w = 3.0f; + v6->spot_weight = v16 * 1.6f * v17 * v22; + if (v6->spot_weight > 0.0f && !v6->query_data[query_index]) v2 = true; } } if (!v2 || reset_exposure) { - //uniform_value[U_EXPOSURE] = 1; - //shaders_ft.set(SHADER_FT_EXPOSURE); - shaders_ft.set(SHADER_FT_EXPOSURE_MEASURE); - shaders_ft.local_frag_set(1, - chara_data[0].field_80, - chara_data[1].field_80, - chara_data[2].field_80, - chara_data[3].field_80); + exposure_measure_shader_data exposure_measure = {}; + exposure_measure.g_spot_weight = { + chara_data[0].spot_weight, + chara_data[1].spot_weight, + chara_data[2].spot_weight, + chara_data[3].spot_weight + }; - post_process_exposure_chara_data* v27 = chara_data; - for (int32_t i = 0; i < 4 && i < ROB_CHARA_COUNT; i++, v27++) - shaders_ft.local_frag_set(4ULL + (size_t)i * 8, 8, v27->field_0); + post_process_exposure_chara_data* chara_data = this->chara_data; + for (int32_t i = 0; i < 4 && i < ROB_CHARA_COUNT; i++, chara_data++) + for (int32_t j = 0; j < 8; j++) + exposure_measure.g_spot_coefficients[i * 8 + j] = chara_data->spot_coefficients[j]; + exposure_measure_ubo.WriteMapMemory(exposure_measure); if (reset_exposure) glViewport(0, 0, 32, 1); else glViewport(exposure_history_counter, 0, 1, 1); exposure_history.bind(); + + uniform_value[U_EXPOSURE] = 1; + shaders_ft.set_opengl_shader(SHADER_FT_EXPOSURE); + exposure_measure_ubo.Bind(1); gl_state_active_bind_texture_2d(0, in_tex_0); gl_state_active_bind_texture_2d(1, in_tex_1); - render_texture::draw_params(&shaders_ft, reset_exposure ? 32 : 1, 1); + render_texture::draw_quad(&shaders_ft, reset_exposure ? 32 : 1, 1); exposure_history_counter++; exposure_history_counter %= 32; } glViewport(0, 0, 1, 1); exposure.bind(); + uniform_value[U_EXPOSURE] = 2; + shaders_ft.set_opengl_shader(SHADER_FT_EXPOSURE); gl_state_active_bind_texture_2d(0, exposure_history.color_texture->tex); - //uniform_value[U_EXPOSURE] = 2; - //shaders_ft.set(SHADER_FT_EXPOSURE); - shaders_ft.set(SHADER_FT_EXPOSURE_AVERAGE); - render_texture::draw_params(&shaders_ft, 1, 1); + render_texture::draw_quad(&shaders_ft, 1, 1); } void post_process_exposure::get_exposure_chara_data(void* pp_data, camera* cam) { - shader::unbind(); + shader_opengl::unbind(); post_process* pp = (post_process*)pp_data; @@ -185,6 +198,10 @@ void post_process_exposure::get_exposure_chara_data(void* pp_data, camera* cam) gl_state_set_depth_mask(GL_FALSE); gl_state_disable_cull_face(); + shaders_ft.set_opengl_shader(SHADER_FT_SUN_NO_TEXTURED); + gl_state_bind_vertex_array(pp->query_vao); + pp->sun_quad_ubo.Bind(0); + post_process_exposure_chara_data* chara = chara_data; int32_t query_index = (this->query_index + 1) % 3; this->query_index = query_index; @@ -201,9 +218,21 @@ void post_process_exposure::get_exposure_chara_data(void* pp_data, camera* cam) mat4_mult(&mat, &cam->view, &mat); mat4_translate_mult(&mat, max_face_depth + 0.1f, 0.0f, -0.06f, &mat); mat4_clear_rot(&mat, &mat); + mat4_scale_rot(&mat, 0.0035f, &mat); mat4_mult(&mat, &cam->projection, &mat); - pp->draw_query_samples(chara->query[next_query_index], 0.0035f, mat); + sun_quad_shader_data shader_data = {}; + mat4_transpose(&mat, &mat); + shader_data.g_transform[0] = mat.row0; + shader_data.g_transform[1] = mat.row1; + shader_data.g_transform[2] = mat.row2; + shader_data.g_transform[3] = mat.row3; + shader_data.g_emission = 0.0f; + pp->sun_quad_ubo.WriteMapMemory(shader_data); + + glBeginQuery(GL_SAMPLES_PASSED, chara->query[next_query_index]); + shaders_ft.draw_arrays(GL_TRIANGLE_STRIP, 0, 4); + glEndQuery(GL_SAMPLES_PASSED); if (chara->query_data[next_query_index] == -1) chara->query_data[next_query_index] = 0; @@ -218,6 +247,8 @@ void post_process_exposure::get_exposure_chara_data(void* pp_data, camera* cam) } } + gl_state_bind_vertex_array(0); + gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); gl_state_set_depth_mask(GL_TRUE); gl_state_enable_cull_face(); diff --git a/src/CRE/post_process/exposure.hpp b/src/CRE/post_process/exposure.hpp index e12c3f3e..e3b531f4 100644 --- a/src/CRE/post_process/exposure.hpp +++ b/src/CRE/post_process/exposure.hpp @@ -7,13 +7,14 @@ #include "../../KKdLib/default.hpp" #include "../../KKdLib/vec.hpp" +#include "../GL/uniform_buffer.hpp" #include "../camera.hpp" #include "../render_texture.hpp" #include "../shared.hpp" struct post_process_exposure_chara_data { - vec4 field_0[8]; - float_t field_80; + vec4 spot_coefficients[8]; + float_t spot_weight; GLuint query[3]; GLuint query_data[3]; @@ -26,6 +27,7 @@ struct post_process_exposure { int32_t exposure_history_counter; post_process_exposure_chara_data* chara_data; int32_t query_index; + GL::UniformBuffer exposure_measure_ubo; post_process_exposure(); ~post_process_exposure(); diff --git a/src/CRE/post_process/tone_map.cpp b/src/CRE/post_process/tone_map.cpp index 9529e6b7..2dacad80 100644 --- a/src/CRE/post_process/tone_map.cpp +++ b/src/CRE/post_process/tone_map.cpp @@ -12,21 +12,22 @@ static void post_process_tone_map_calculate_data(post_process_tone_map* tm); static void post_process_tone_map_calculate_tex(post_process_tone_map* tm); -post_process_tone_map_data::post_process_tone_map_data() : tex(), exposure(), auto_exposure(), gamma(), +post_process_tone_map_data::post_process_tone_map_data() : tex_data(), exposure(), auto_exposure(), gamma(), gamma_rate(), saturate_power(), saturate_coeff(), scene_fade_blend_func(), tone_map_method(), lens_flare(), lens_shaft(), lens_ghost(), lens_flare_power(), lens_flare_appear_power(), update(), update_tex() { } -post_process_tone_map::post_process_tone_map() : shader_data(), tone_map() { - +post_process_tone_map::post_process_tone_map() : shader_data(), tone_map_ubo(), tone_map_tex() { + tone_map_ubo.Create(sizeof(post_process_tone_map_shader_data)); } post_process_tone_map::~post_process_tone_map() { if (!this) return; - glDeleteTextures(1, &tone_map); + tone_map_ubo.Destroy(); + glDeleteTextures(1, &tone_map_tex); } void post_process_tone_map::apply(render_texture* in_tex, texture* light_proj_tex, texture* back_2d_tex, @@ -49,12 +50,12 @@ void post_process_tone_map::apply(render_texture* in_tex, texture* light_proj_te post_process_tone_map_calculate_tex(this); if (GLAD_GL_VERSION_4_5) - glTextureSubImage2D(tone_map, 0, 0, 0, - 16 * POST_PROCESS_TONE_MAP_SAT_GAMMA_SAMPLES, 1, GL_RG, GL_FLOAT, data.tex); + glTextureSubImage2D(tone_map_tex, 0, 0, 0, + 16 * POST_PROCESS_TONE_MAP_SAT_GAMMA_SAMPLES, 1, GL_RG, GL_FLOAT, data.tex_data); else { - gl_state_bind_texture_2d(tone_map); + gl_state_bind_texture_2d(tone_map_tex); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, - 16 * POST_PROCESS_TONE_MAP_SAT_GAMMA_SAMPLES, 1, GL_RG, GL_FLOAT, data.tex); + 16 * POST_PROCESS_TONE_MAP_SAT_GAMMA_SAMPLES, 1, GL_RG, GL_FLOAT, data.tex_data); gl_state_bind_texture_2d(0); } data.update_tex = false; @@ -69,7 +70,7 @@ void post_process_tone_map::apply(render_texture* in_tex, texture* light_proj_te gl_state_active_bind_texture_2d(0, in_tex->color_texture->tex); gl_state_active_bind_texture_2d(1, in_tex_0); - gl_state_active_bind_texture_2d(2, tone_map); + gl_state_active_bind_texture_2d(2, tone_map_tex); gl_state_active_bind_texture_2d(3, in_tex_1); if (back_2d_tex) { @@ -82,13 +83,18 @@ void post_process_tone_map::apply(render_texture* in_tex, texture* light_proj_te uniform_value[U_LIGHT_PROJ] = 1; } + shader_data.g_texcoord_transforms[0] = { 1.0f, 0.0f, 0.0f, 0.0f }; + shader_data.g_texcoord_transforms[1] = { 0.0f, 1.0f, 0.0f, 0.0f }; + shader_data.g_texcoord_transforms[2] = { 1.0f, 0.0f, 0.0f, 0.0f }; + shader_data.g_texcoord_transforms[3] = { 0.0f, 1.0f, 0.0f, 0.0f }; + if (pp->lens_flare_texture) { static const vec4 border_color = 0.0f; const float_t aspect = (float_t)pp->render_height / (float_t)pp->render_width; uniform_value[U_FLARE] = 1; - gl_state_active_bind_texture_2d(4, pp->lens_flare_texture); + gl_state_active_bind_texture_2d(4, pp->lens_flare_texture->tex); glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, (GLfloat*)&border_color); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); @@ -101,11 +107,13 @@ void post_process_tone_map::apply(render_texture* in_tex, texture* light_proj_te (pp->lens_flare_pos.y - (float_t)pp->render_height) * (1.0f / (float_t)pp->render_width), 0.0f, &mat); mat4_scale_rot(&mat, 1.0f, aspect, 1.0f, &mat); - shaders_ft.state_matrix_set_texture(4, mat); + mat4_transpose(&mat, &mat); + shader_data.g_texcoord_transforms[4] = mat.row0; + shader_data.g_texcoord_transforms[5] = mat.row1; if (pp->lens_shaft_scale < 50.0f) { uniform_value[U_FLARE] = 2; - gl_state_active_bind_texture_2d(5, pp->lens_shaft_texture); + gl_state_active_bind_texture_2d(5, pp->lens_shaft_texture->tex); glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, (GLfloat*)&border_color); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); @@ -117,8 +125,20 @@ void post_process_tone_map::apply(render_texture* in_tex, texture* light_proj_te (pp->lens_flare_pos.y - (float_t)pp->render_height) * (1.0f / (float_t)pp->render_width), 0.0f, &mat); mat4_scale_rot(&mat, 1.0f, aspect, 1.0f, &mat); - shaders_ft.state_matrix_set_texture(5, mat); + mat4_transpose(&mat, &mat); + shader_data.g_texcoord_transforms[6] = mat.row0; + shader_data.g_texcoord_transforms[7] = mat.row1; } + else { + shader_data.g_texcoord_transforms[6] = { 1.0f, 0.0f, 0.0f, 0.0f }; + shader_data.g_texcoord_transforms[7] = { 0.0f, 1.0f, 0.0f, 0.0f }; + } + } + else { + shader_data.g_texcoord_transforms[4] = { 1.0f, 0.0f, 0.0f, 0.0f }; + shader_data.g_texcoord_transforms[5] = { 0.0f, 1.0f, 0.0f, 0.0f }; + shader_data.g_texcoord_transforms[6] = { 1.0f, 0.0f, 0.0f, 0.0f }; + shader_data.g_texcoord_transforms[7] = { 0.0f, 1.0f, 0.0f, 0.0f }; } if (npr_param == 1) { @@ -132,18 +152,14 @@ void post_process_tone_map::apply(render_texture* in_tex, texture* light_proj_te glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } - + + tone_map_ubo.WriteMapMemory(shader_data); + glViewport(0, 0, rt->color_texture->width, rt->color_texture->height); buf_rt->bind(); - shaders_ft.set(SHADER_FT_TONEMAP); - shaders_ft.local_vert_set(1, shader_data.p_exposure); - shaders_ft.local_frag_set(1, shader_data.p_flare_coef); - shaders_ft.local_frag_set(2, shader_data.p_fade_color); - shaders_ft.local_frag_set(4, shader_data.p_tone_scale); - shaders_ft.local_frag_set(5, shader_data.p_tone_offset); - shaders_ft.local_frag_set(6, shader_data.p_fade_func); - shaders_ft.local_frag_set(7, shader_data.p_inv_tone); - render_texture::draw_custom(&shaders_ft); + shaders_ft.set_opengl_shader(SHADER_FT_TONEMAP); + tone_map_ubo.Bind(1); + render_texture::draw(&shaders_ft); gl_state_active_bind_texture_2d(2, 0); fbo::blit(buf_rt->fbos[0], rt->fbos[0], @@ -155,23 +171,23 @@ void post_process_tone_map::init_fbo() { if (!this) return; - if (!tone_map) { + if (!tone_map_tex) { if (GLAD_GL_VERSION_4_5) { - glCreateTextures(GL_TEXTURE_2D, 1, &tone_map); + glCreateTextures(GL_TEXTURE_2D, 1, &tone_map_tex); - glTextureStorage2D(tone_map, 1, GL_RG16F, 16 * POST_PROCESS_TONE_MAP_SAT_GAMMA_SAMPLES, 1); - glTextureParameteri(tone_map, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTextureParameteri(tone_map, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTextureStorage2D(tone_map_tex, 1, GL_RG16F, 16 * POST_PROCESS_TONE_MAP_SAT_GAMMA_SAMPLES, 1); + glTextureParameteri(tone_map_tex, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTextureParameteri(tone_map_tex, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); const GLint swizzle[] = { GL_RED, GL_RED, GL_RED, GL_GREEN }; - glTextureParameteri(tone_map, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTextureParameteri(tone_map, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTextureParameteriv(tone_map, GL_TEXTURE_SWIZZLE_RGBA, swizzle); + glTextureParameteri(tone_map_tex, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTextureParameteri(tone_map_tex, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTextureParameteriv(tone_map_tex, GL_TEXTURE_SWIZZLE_RGBA, swizzle); } else { - glGenTextures(1, &tone_map); + glGenTextures(1, &tone_map_tex); - gl_state_bind_texture_2d(tone_map); + gl_state_bind_texture_2d(tone_map_tex); glTexStorage2D(GL_TEXTURE_2D, 1, GL_RG16F, 16 * POST_PROCESS_TONE_MAP_SAT_GAMMA_SAMPLES, 1); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); @@ -441,47 +457,48 @@ static void post_process_tone_map_calculate_data(post_process_tone_map* tm) { vec3 tone_trans_scale = vec3::rcp(tm->data.tone_trans_end - tm->data.tone_trans_start); vec3 tone_trans_offset = -(tone_trans_scale * tm->data.tone_trans_start); - post_process_tone_map_shader_data* v = &tm->shader_data; - v->p_exposure.x = tm->data.exposure; - v->p_exposure.y = 0.0625f; - v->p_exposure.z = tm->data.exposure * 0.5f; - v->p_exposure.w = tm->data.auto_exposure ? 1.0f : 0.0f; - v->p_flare_coef.x = (tm->data.lens_flare * 2.0f) + post_process_tone_map_shader_data* tmshd = &tm->shader_data; + tmshd->g_exposure.x = tm->data.exposure; + tmshd->g_exposure.y = 0.0625f; + tmshd->g_exposure.z = tm->data.exposure * 0.5f; + tmshd->g_exposure.w = tm->data.auto_exposure ? 1.0f : 0.0f; + tmshd->g_flare_coef.x = (tm->data.lens_flare * 2.0f) * (tm->data.lens_flare_appear_power + tm->data.lens_flare_power); - v->p_flare_coef.y = tm->data.lens_shaft * 2.0f; - v->p_flare_coef.z = 0.0f; - v->p_flare_coef.w = 0.0f; - v->p_fade_color = tm->data.scene_fade; + tmshd->g_flare_coef.y = tm->data.lens_shaft * 2.0f; + tmshd->g_flare_coef.z = 0.0f; + tmshd->g_flare_coef.w = 0.0f; + tmshd->g_fade_color = tm->data.scene_fade; if (tm->data.scene_fade_blend_func == 1 || tm->data.scene_fade_blend_func == 2) - *(vec3*)&v->p_fade_color = *(vec3*)&v->p_fade_color * v->p_fade_color.w; - v->p_tone_scale.x = tone_trans_scale.x; - v->p_tone_scale.y = tone_trans_scale.y; - v->p_tone_scale.z = tone_trans_scale.z; - v->p_tone_offset.x = tone_trans_offset.x; - v->p_tone_offset.y = tone_trans_offset.y; - v->p_tone_offset.z = tone_trans_offset.z; - v->p_fade_func.x = (float_t)tm->data.scene_fade_blend_func; - v->p_inv_tone.x = tm->data.gamma > 0.0f ? 2.0f / (tm->data.gamma * 3.0f) : 0.0f; + *(vec3*)&tmshd->g_fade_color = *(vec3*)&tmshd->g_fade_color * tmshd->g_fade_color.w; + tmshd->g_tone_scale.x = tone_trans_scale.x; + tmshd->g_tone_scale.y = tone_trans_scale.y; + tmshd->g_tone_scale.z = tone_trans_scale.z; + tmshd->g_tone_offset.x = tone_trans_offset.x; + tmshd->g_tone_offset.y = tone_trans_offset.y; + tmshd->g_tone_offset.z = tone_trans_offset.z; + tmshd->g_tone_scale.w = (float_t)tm->data.scene_fade_blend_func; + tmshd->g_tone_offset.w = tm->data.gamma > 0.0f ? 2.0f / (tm->data.gamma * 3.0f) : 0.0f; } static void post_process_tone_map_calculate_tex(post_process_tone_map* tm) { - const float_t post_process_tone_map__scale = (float_t)(1.0 / (double_t)POST_PROCESS_TONE_MAP_SAT_GAMMA_SAMPLES); - const int32_t post_process_tone_map__size = 16 * POST_PROCESS_TONE_MAP_SAT_GAMMA_SAMPLES; + const float_t post_process_tone_map_scale = (float_t)(1.0 / (double_t)POST_PROCESS_TONE_MAP_SAT_GAMMA_SAMPLES); + const int32_t post_process_tone_map_size = 16 * POST_PROCESS_TONE_MAP_SAT_GAMMA_SAMPLES; int32_t i, j; int32_t saturate_power; float_t saturate_coeff; - vec2* tex = tm->data.tex; + vec2* tex_data = tm->data.tex_data; float_t gamma_power, gamma, saturation; gamma_power = tm->data.gamma * tm->data.gamma_rate * 1.5f; saturate_power = tm->data.saturate_power; saturate_coeff = tm->data.saturate_coeff; - tex[0].x = 0.0f; - tex[0].y = 0.0f; - for (i = 1; i < post_process_tone_map__size; i++) { - gamma = powf(1.0f - expf(-i * post_process_tone_map__scale), gamma_power); + tex_data->x = 0.0f; + tex_data->y = 0.0f; + tex_data++; + for (i = 1; i < post_process_tone_map_size; i++, tex_data++) { + gamma = powf(1.0f - expf(-i * post_process_tone_map_scale), gamma_power); saturation = gamma * 2.0f - 1.0f; for (j = 0; j < saturate_power; j++) { saturation *= saturation; @@ -490,8 +507,8 @@ static void post_process_tone_map_calculate_tex(post_process_tone_map* tm) { saturation *= saturation; } - tex[i].x = gamma; - tex[i].y = gamma * saturate_coeff + tex_data->x = gamma; + tex_data->y = gamma * saturate_coeff * ((float_t)POST_PROCESS_TONE_MAP_SAT_GAMMA_SAMPLES / (float_t)i) * (1.0f - saturation); } } diff --git a/src/CRE/post_process/tone_map.hpp b/src/CRE/post_process/tone_map.hpp index 745c330d..8c9ec3a3 100644 --- a/src/CRE/post_process/tone_map.hpp +++ b/src/CRE/post_process/tone_map.hpp @@ -8,23 +8,23 @@ #include "../../KKdLib/default.hpp" #include "../../KKdLib/light_param/glow.hpp" #include "../../KKdLib/vec.hpp" +#include "../GL/uniform_buffer.hpp" #include "../shared.hpp" #include "../render_texture.hpp" #define POST_PROCESS_TONE_MAP_SAT_GAMMA_SAMPLES 32 struct post_process_tone_map_shader_data { - vec4 p_exposure; - vec4 p_flare_coef; - vec4 p_fade_color; - vec4 p_tone_scale; - vec4 p_tone_offset; - vec4 p_fade_func; - vec4 p_inv_tone; + vec4 g_exposure; + vec4 g_flare_coef; + vec4 g_fade_color; + vec4 g_tone_scale; //xyz=tone_scale, w=fade_func + vec4 g_tone_offset; //xyz=tone_offset, w=inv_tone + vec4 g_texcoord_transforms[8]; }; struct post_process_tone_map_data { - vec2 tex[16 * POST_PROCESS_TONE_MAP_SAT_GAMMA_SAMPLES]; + vec2 tex_data[16 * POST_PROCESS_TONE_MAP_SAT_GAMMA_SAMPLES]; float_t exposure; bool auto_exposure; float_t gamma; @@ -50,7 +50,8 @@ struct post_process_tone_map_data { struct post_process_tone_map { post_process_tone_map_data data; post_process_tone_map_shader_data shader_data; - GLuint tone_map; + GL::UniformBuffer tone_map_ubo; + GLuint tone_map_tex; post_process_tone_map(); ~post_process_tone_map(); diff --git a/src/CRE/pv_db.cpp b/src/CRE/pv_db.cpp index 367995c5..7e3c1106 100644 --- a/src/CRE/pv_db.cpp +++ b/src/CRE/pv_db.cpp @@ -4,10 +4,11 @@ */ #include "pv_db.hpp" -#include "mdata_manager.hpp" -#include "render_context.hpp" #include "../KKdLib/hash.hpp" #include "../KKdLib/str_utils.hpp" +#include "hand_item.hpp" +#include "mdata_manager.hpp" +#include "render_context.hpp" extern render_context* rctx_ptr; @@ -1283,7 +1284,7 @@ namespace pv_db { pv_db_pv_hand_item hand_item; hand_item.index = (int32_t)k; - hand_item.id = -1; + hand_item.id = hand_item_handler_data_get_hand_item_uid(name.c_str()); hand_item.name = name; d.hand_item.push_back(hand_item); kv.close_scope(); @@ -2002,28 +2003,31 @@ void task_pv_db_init() { void task_pv_db_add_paths() { pv_db::TaskPvDB* task_pv_db = task_pv_db_get(); - std::list& prefixes = mdata_manager_get()->prefixes; - for (std::string& i : prefixes) { - std::string pv_db_file = i + "pv_db.txt"; + for (const std::string& i : mdata_manager_get()->GetPrefixes()) { + std::string pv_db_file; + pv_db_file.assign(i); + pv_db_file.append("pv_db.txt"); if (data_list[DATA_AFT].check_file_exists("rom/", pv_db_file.c_str())) task_pv_db->paths.push_back({ "rom/", pv_db_file }); - std::string pv_field_file = i + "pv_field.txt"; + std::string pv_field_file; + pv_field_file.assign(i); + pv_field_file.append("pv_field.txt"); if (data_list[DATA_AFT].check_file_exists("rom/", pv_field_file.c_str())) task_pv_db->paths.push_back({ "rom/", pv_field_file }); } } -bool task_pv_db_append_task() { - return app::TaskWork::AppendTask(task_pv_db_get(), "PV DB"); +bool task_pv_db_add_task() { + return app::TaskWork::AddTask(task_pv_db_get(), "PV DB"); } void task_pv_db_free_pv_data() { task_pv_db_get()->pv_data.clear(); } -bool task_pv_db_free_task() { - return task_pv_db_get()->SetDest(); +bool task_pv_db_del_task() { + return task_pv_db_get()->DelTask(); } pv_db::TaskPvDB* task_pv_db_get() { diff --git a/src/CRE/pv_db.hpp b/src/CRE/pv_db.hpp index 5b7f7567..feba20ee 100644 --- a/src/CRE/pv_db.hpp +++ b/src/CRE/pv_db.hpp @@ -529,9 +529,9 @@ namespace pv_db { extern void task_pv_db_init(); extern void task_pv_db_add_paths(); -extern bool task_pv_db_append_task(); +extern bool task_pv_db_add_task(); extern void task_pv_db_free_pv_data(); -extern bool task_pv_db_free_task(); +extern bool task_pv_db_del_task(); extern pv_db::TaskPvDB* task_pv_db_get(); extern uint32_t task_pv_db_get_paths_count(); extern pv_db_pv* task_pv_db_get_pv(int32_t pv_id); diff --git a/src/CRE/pv_expression.cpp b/src/CRE/pv_expression.cpp index 6608b0fd..938b6314 100644 --- a/src/CRE/pv_expression.cpp +++ b/src/CRE/pv_expression.cpp @@ -14,7 +14,7 @@ struct pv_expression_file { p_file_handler file_handler; pv_exp* data; - prj::shared_ptr alloc_handler; + prj::shared_ptr alloc_handler; int32_t load_count; uint32_t hash; @@ -66,8 +66,8 @@ pv_expression_file::~pv_expression_file() { } void pv_expression_file::load_file(pv_expression_file* exp_file, const void* data, size_t size) { - prj::shared_ptr& alloc = exp_file->alloc_handler; - alloc = prj::shared_ptr(new alloc_data); + prj::shared_ptr& alloc = exp_file->alloc_handler; + alloc = prj::shared_ptr(new prj::stack_allocator); exp_file->data->unpack_file(alloc, data, size, exp_file->data->modern); } diff --git a/src/CRE/render_context.cpp b/src/CRE/render_context.cpp index f4fc616e..e99f4c78 100644 --- a/src/CRE/render_context.cpp +++ b/src/CRE/render_context.cpp @@ -4,10 +4,10 @@ */ #include "render_context.hpp" +#include "../KKdLib/sort.hpp" #include "Glitter/glitter.hpp" #include "rob/rob.hpp" -#include "draw_pass.hpp" -#include "draw_task.hpp" +#include "render_manager.hpp" #include "file_handler.hpp" #include "shader_ft.hpp" #include "sound.hpp" @@ -16,7 +16,9 @@ float_t delta_frame_history = 0; int32_t delta_frame_history_int = 0; -static void object_data_get_vertex_attrib_buffer_bindings(draw_object* draw, +extern render_context* rctx_ptr; + +static void object_data_get_vertex_attrib_buffer_bindings(const mdl::ObjSubMeshArgs* args, int32_t texcoord_array[2], GLuint vertex_attrib_buffer_binding[16]); static void render_context_light_param_data_ibl_set_diffuse(light_param_ibl_diffuse* diffuse, int32_t level); @@ -57,6 +59,7 @@ static const struc_189 stru_140A24420[] = { { GL_TEXTURE_2D, 0x100, 0x100, 0, GL_RGBA8, GL_ZERO }, { GL_TEXTURE_2D, 0x100, 0x100, 0, GL_RGBA32F, GL_ZERO }, { GL_TEXTURE_2D, 0x100, 0x100, 0, GL_RGBA8, GL_ZERO }, + { GL_TEXTURE_2D, 0x200, 0x100, 0, GL_RGBA16F, GL_ZERO }, // Extra for buf }; static const int32_t stru_140A244E0[][3] = { @@ -71,6 +74,7 @@ static const int32_t stru_140A244E0[][3] = { { 5, 5, 5 }, { 6, 6, 6 }, { 7, 7, 7 }, + { 8, 8, 8 }, // Extra for buf }; draw_state_stats::draw_state_stats() : object_draw_count(), object_translucent_draw_count(), @@ -104,174 +108,6 @@ sss_data::~sss_data() { } -draw_pass::draw_pass() : enable(), reflect_blur_num(), reflect_blur_filter(), -wait_for_gpu(), cpu_time(), gpu_time(), time(), draw_pass_3d(), reflect_type(), tex_index(), -multisample_framebuffer(), multisample_renderbuffer(), multisample(), show_vector_flags(), -show_vector_length(), show_vector_z_offset(), field_2F8(), effect_texture(), npr_param(), -field_31C(), field_31D(), field_31E(), field_31F(), field_320(), npr(), samplers() { - for (bool& i : enable) - i = true; - reflect = true; - refract = true; - shadow = true; - opaque_z_sort = true; - alpha_z_sort = true; - for (bool& i : draw_pass_3d) - i = true; - - shadow_ptr = new ::shadow; - if (shadow_ptr) - shadow_ptr->init_data(); - - for (int32_t i = 0; i < 8; i++) { - const struc_189* v2 = &stru_140A24420[i]; - if (v2->type != GL_TEXTURE_2D) - continue; - - render_texture& rt = render_textures[i]; - rt.init(v2->width, v2->height, v2->max_level, v2->color_format, v2->depth_format); - rt.bind(); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - } - gl_state_bind_framebuffer(0); - - glCreateSamplers(18, samplers); - for (int32_t i = 0; i < 18; i++) { - GLuint sampler = samplers[i]; - - static const vec4 border_color = 0.0f; - glSamplerParameterfv(sampler, GL_TEXTURE_BORDER_COLOR, (GLfloat*)&border_color); - glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, - i % 2 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); - glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - - GLuint wrap_s; - switch (i / 2 % 3) { - case 0: - wrap_s = GL_CLAMP_TO_EDGE; - break; - case 1: - wrap_s = GL_REPEAT; - break; - case 2: - wrap_s = GL_MIRRORED_REPEAT; - break; - } - glSamplerParameteri(sampler, GL_TEXTURE_WRAP_S, wrap_s); - - GLenum wrap_t; - switch (i / 6 % 3) { - case 0: - wrap_t = GL_CLAMP_TO_EDGE; - break; - case 1: - wrap_t = GL_REPEAT; - break; - case 2: - wrap_t = GL_MIRRORED_REPEAT; - break; - } - glSamplerParameteri(sampler, GL_TEXTURE_WRAP_T, wrap_t); - } -} - -draw_pass::~draw_pass() { - delete shadow_ptr; - - if (multisample_renderbuffer) { - glDeleteRenderbuffers(1, &multisample_renderbuffer); - multisample_renderbuffer = 0; - } - - if (multisample_framebuffer) { - glDeleteFramebuffers(1, &multisample_framebuffer); - multisample_framebuffer = 0; - } -} - -void draw_pass::add_preprocess(int32_t type, void(*func)(void*), void* data) { - preprocess.push_back({ type, func, data }); -} - -void draw_pass::clear_preprocess(int32_t type) { - for (std::list::iterator i = preprocess.begin(); i != preprocess.end(); i++) - if (i->type == type) { - preprocess.erase(i); - break; - } -} - -render_texture& draw_pass::get_render_texture(int32_t index) { - return render_textures[stru_140A244E0[index][tex_index[index]]]; -} - -void draw_pass::resize(int32_t width, int32_t height) { - if (!multisample_framebuffer) - glGenFramebuffers(1, &multisample_framebuffer); - - if (!multisample_renderbuffer) - glGenRenderbuffers(1, &multisample_renderbuffer); - - glBindFramebuffer(GL_FRAMEBUFFER, multisample_framebuffer); - glBindRenderbuffer(GL_RENDERBUFFER, multisample_renderbuffer); - glRenderbufferStorageMultisample(GL_RENDERBUFFER, 8, GL_RGBA8, width, height); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, multisample_renderbuffer); - glDrawBuffer(GL_COLOR_ATTACHMENT0); - glReadBuffer(GL_COLOR_ATTACHMENT0); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glBindRenderbuffer(GL_RENDERBUFFER, 0); -} - -void draw_pass::set_enable(draw_pass_type type, bool value) { - enable[type] = value; -} - -void draw_pass::set_effect_texture(texture* value) { - effect_texture = value; -} - -void draw_pass::set_multisample(bool value) { - multisample = value; -} - -void draw_pass::set_npr_param(int32_t value) { - npr_param = value; -} - -void draw_pass::set_reflect(bool value) { - reflect = value; -} - -void draw_pass::set_reflect_blur(int32_t reflect_blur_num, blur_filter_mode reflect_blur_filter) { - this->reflect_blur_num = reflect_blur_num; - this->reflect_blur_filter = reflect_blur_filter; -} - -void draw_pass::set_reflect_resolution_mode(reflect_refract_resolution_mode mode) { - tex_index[0] = mode; -} - -void draw_pass::set_reflect_type(stage_data_reflect_type type) { - reflect_type = type; -} - -void draw_pass::set_refract(bool value) { - refract = value; -} - -void draw_pass::set_refract_resolution_mode(reflect_refract_resolution_mode mode) { - tex_index[1] = mode; -} - -void draw_pass::set_shadow_false() { - shadow = false; -} - -void draw_pass::set_shadow_true() { - shadow = true; -} - draw_state::draw_state() : wireframe(), wireframe_overlay(), light(), self_shadow(), field_45(), use_global_material(), fog_height(), ex_data_mat(), field_68() { shader = true; @@ -317,6 +153,76 @@ texture_transform_struct::texture_transform_struct(uint32_t id, mat4& mat) : id( } +namespace mdl { + void ObjData::init_sub_mesh(DispManager* disp_manager, const mat4* mat, + float_t radius, obj_sub_mesh* sub_mesh, obj_mesh* mesh, obj_material_data* material, + std::vector* textures, int32_t mat_count, mat4* mats, GLuint array_buffer, + GLuint element_array_buffer, vec4* blend_color, vec4* emission, int32_t morph_array_buffer, + int32_t instances_count, mat4* instances_mat, void(*func)(const ObjSubMeshArgs*)) { + kind = mdl::OBJ_KIND_NORMAL; + this->mat = *mat; + this->radius = radius; + + mdl::ObjSubMeshArgs* args = &this->args.sub_mesh; + args->mesh = mesh; + args->morph_weight = disp_manager->morph.weight; + args->material = material; + args->sub_mesh = sub_mesh; + args->textures = textures; + args->mat_count = mat_count; + args->mats = mats; + args->array_buffer = array_buffer; + args->element_array_buffer = element_array_buffer; + args->morph_array_buffer = morph_array_buffer; + + args->texture_pattern_count = disp_manager->texture_pattern_count; + for (int32_t i = 0; i < disp_manager->texture_pattern_count && i < TEXTURE_PATTERN_COUNT; i++) + args->texture_pattern_array[i] = disp_manager->texture_pattern_array[i]; + + args->texture_transform_count = disp_manager->texture_transform_count; + for (int32_t i = 0; i < disp_manager->texture_transform_count && i < TEXTURE_TRANSFORM_COUNT; i++) + args->texture_transform_array[i] = disp_manager->texture_transform_array[i]; + + if (blend_color && *blend_color != 1.0f) { + args->set_blend_color = true; + args->blend_color = *blend_color; + } + else { + args->set_blend_color = false; + args->blend_color = 1.0f; + } + + args->emission = *emission; + + args->chara_color = disp_manager->chara_color; + args->self_shadow = disp_manager->draw_task_flags & (DRAW_TASK_8 | DRAW_TASK_4) ? 1 : 0; + args->shadow = disp_manager->shadow_type; + args->texture_color_coefficients = disp_manager->texture_color_coefficients; + args->texture_color_coefficients.w = disp_manager->wet_param; + args->texture_color_offset = disp_manager->texture_color_offset; + args->texture_specular_coefficients = disp_manager->texture_specular_coefficients; + args->texture_specular_offset = disp_manager->texture_specular_offset; + args->instances_count = instances_count; + args->instances_mat = instances_mat; + args->func = func; + + disp_manager->add_vertex_array(args); + } + + void ObjData::init_translucent(const mat4* mat, ObjTranslucentArgs* translucent) { + kind = mdl::OBJ_KIND_TRANSLUCENT; + this->mat = *mat; + args.translucent = *translucent; + } + + void ObjData::init_user(const mat4* mat, UserArgsFunc func, void* data) { + kind = OBJ_KIND_USER; + this->mat = *mat; + args.user.func = func; + args.user.data = data; + } +} + light_proj::light_proj(int32_t width, int32_t height) : enable(), texture_id() { shadow_texture[0].init(2048, 512, 0, GL_R32F, GL_DEPTH_COMPONENT32F); shadow_texture[1].init(2048, 512, 0, GL_R32F, GL_ZERO); @@ -365,33 +271,6 @@ bool light_proj::set(render_context* rctx) { return false; } -void light_proj::get_proj_mat(vec3* view_point, vec3* interest, float_t fov, mat4* mat) { - if (mat) { - mat4 temp; - mat4_translate(0.5f, 0.5f, 0.5f, &temp); - mat4_scale_rot(&temp, 0.5f, 0.5f, 0.5f, &temp); - - mat4 proj; - mat4_persp(fov, 4.0, 0.1000000014901161, 10.0, &proj); - mat4_mult(&proj, &temp, &proj); - - mat4 view; - vec3 up = { 0.0f, 1.0f, 0.0f }; - mat4_look_at(view_point, interest, &up, &view); - mat4_mult(&view, &proj, mat); - } - else { - mat4 proj; - mat4_persp(fov, 4.0, 0.1000000014901161, 10.0, &proj); - - mat4 view; - vec3 up = { 0.0f, 1.0f, 0.0f }; - mat4_look_at(view_point, interest, &up, &view); - - shaders_ft.state_matrix_set_mvp(view, proj); - } -} - bool light_proj::set_mat(render_context* rctx, bool set_mat) { light_set* set = &rctx->light_set[LIGHT_SET_MAIN]; light_data* data = &set->lights[LIGHT_PROJECTION]; @@ -407,497 +286,275 @@ bool light_proj::set_mat(render_context* rctx, bool set_mat) { return false; float_t spot_cutoff = data->get_spot_cutoff(); - float_t fov = atanf(tanf(spot_cutoff * DEG_TO_RAD_FLOAT) * 0.25f) * (RAD_TO_DEG_FLOAT * 2.0f); + float_t fov = atanf(tanf(spot_cutoff * DEG_TO_RAD_FLOAT) * 0.25f) * 2.0f; vec3 interest = position + spot_direction; if (set_mat) { + mat4 temp; + mat4_translate(0.5f, 0.5f, 0.5f, &temp); + mat4_scale_rot(&temp, 0.5f, 0.5f, 0.5f, &temp); + + mat4 proj; + mat4_persp(fov, 4.0f, 0.1f, 10.0f, &proj); + mat4_mult(&proj, &temp, &proj); + + mat4 view; + vec3 up = { 0.0f, 1.0f, 0.0f }; + mat4_look_at(&position, &interest, &up, &view); + mat4 mat; - get_proj_mat(&position, &interest, fov, &mat); - shaders_ft.env_vert_set(24, 4, &mat.row0); + mat4_mult(&view, &proj, &mat); + rctx->obj_scene.set_g_light_projection(mat); + } + else { + mat4_persp(fov, 4.0f, 0.1f, 10.0f, &rctx->proj_mat); + + vec3 up = { 0.0f, 1.0f, 0.0f }; + mat4_look_at(&position, &interest, &up, &rctx->view_mat); + rctx->obj_scene.set_projection_view(rctx->view_mat, rctx->proj_mat); } - else - get_proj_mat(&position, &interest, fov, 0); return true; } -morph_struct::morph_struct() : value() { +morph_struct::morph_struct() : weight() { } -object_data_buffer::object_data_buffer() { - offset = 0; - max_offset = 0; - size = 0x300000; - data = force_malloc(0x300000); +texture_data_struct::texture_data_struct() : field_0() { + } -object_data_buffer::~object_data_buffer() { - free_def(data); -} - -draw_task* object_data_buffer::add_draw_task(draw_task_type type) { - if (!data) - return 0; - - int32_t size = align_val(sizeof(draw_task_type) + sizeof(mat4) + sizeof(float_t) * 2, 0x08); - switch (type) { - case DRAW_TASK_OBJECT: - size += sizeof(draw_object); - break; - case DRAW_TASK_OBJECT_PRIMITIVE: - size += sizeof(draw_primitive); - break; - case DRAW_TASK_OBJECT_PREPROCESS: - size += sizeof(draw_task_preprocess); - break; - case DRAW_TASK_OBJECT_TRANSLUCENT: - size += sizeof(draw_task_object_translucent); - break; - default: - return 0; +namespace mdl { + DispManager::DispManager() : draw_task_flags(), shadow_type(), field_8(), field_C(), + culling(), show_alpha_center(), show_mat_center(), texture_pattern_count(), + texture_pattern_array(), wet_param(), texture_transform_count(), + texture_transform_array(), material_list_count(), material_list_array() { + field_230 = -1; + object_culling = true; + object_sort = true; + chara_color = true; + buff_offset = 0; + buff_max = 0; + buff_size = 0x300000; + buff = force_malloc(0x300000); + texture_color_coefficients = 1.0f; + texture_color_offset = 0.0f; + texture_specular_coefficients = 1.0f; + texture_specular_offset = 0.0f; } - if (offset + size > this->size) - return 0; - - draw_task* task = (draw_task*)((size_t)data + offset); - offset += size; - if (max_offset < offset) - max_offset = offset; - return task; -} - -mat4* object_data_buffer::add_mat4(int32_t count) { - if (!data) - return 0; - - int32_t size = sizeof(mat4) * count; - if (offset + size > this->size) - return 0; - - mat4* mats = (mat4*)((size_t)data + offset); - offset += size; - if (max_offset < offset) - max_offset = offset; - return mats; -} - -void object_data_buffer::reset() { - offset = 0; -} - -object_data::object_data() : draw_task_flags(), shadow_type(), field_8(), field_C(), passed(), culled(), -passed_prev(), culled_prev(), show_alpha_center(), show_mat_center(), buffer(), texture_pattern_count(), -texture_pattern_array(), wet_param(), texture_transform_count(), texture_transform_array(), -material_list_count(), material_list_array(), object_bounding_sphere_check_func() { - field_230 = -1; - object_culling = true; - object_sort = true; - chara_color = true; - texture_color_coeff = 1.0f; - texture_color_offset = 0.0f; - texture_specular_coeff = 1.0f; - texture_specular_offset = 0.0f; -} - -object_data::~object_data() { - for (object_data_vertex_array& i : vertex_array_cache) - glDeleteVertexArrays(1, &i.vertex_array); - vertex_array_cache.clear(); -} - -void object_data::add_vertex_array(draw_object* draw) { - obj_mesh* mesh = draw->mesh; - obj_sub_mesh* sub_mesh = draw->sub_mesh; - obj_material_data* material = draw->material; - - GLuint array_buffer = draw->array_buffer; - GLuint morph_array_buffer = draw->morph_array_buffer; - GLuint element_array_buffer = draw->element_array_buffer; - - int32_t texcoord_array[2] = { -1, -1 }; - int32_t color_tex_index = 0; - for (obj_material_texture_data& i : material->material.texdata) { - if (i.tex_index == -1) - continue; - - int32_t texcoord_index = obj_material_texture_type_get_texcoord_index( - i.shader_info.m.tex_type, color_tex_index); - if (texcoord_index < 0) - continue; - - texcoord_array[texcoord_index] = sub_mesh->uv_index[&i - material->material.texdata]; - - if (i.shader_info.m.tex_type == OBJ_MATERIAL_TEXTURE_COLOR) - color_tex_index++; + DispManager::~DispManager() { + for (DispManager::vertex_array& i : vertex_array_cache) + glDeleteVertexArrays(1, &i.vertex_array); + vertex_array_cache.clear(); + free_def(buff); } - GLuint vertex_attrib_buffer_binding[16] = {}; - object_data_get_vertex_attrib_buffer_bindings(draw, - texcoord_array, vertex_attrib_buffer_binding); + void DispManager::add_vertex_array(const ObjSubMeshArgs* args) { + const obj_mesh* mesh = args->mesh; + const obj_sub_mesh* sub_mesh = args->sub_mesh; + const obj_material_data* material = args->material; - bool compressed = mesh->attrib.m.compressed; - GLsizei size_vertex = (GLsizei)mesh->size_vertex; - obj_vertex_format vertex_format = mesh->vertex_format; + GLuint array_buffer = args->array_buffer; + GLuint morph_array_buffer = args->morph_array_buffer; + GLuint element_array_buffer = args->element_array_buffer; - object_data_vertex_array* vertex_array = 0; - for (object_data_vertex_array& i : vertex_array_cache) - if (i.alive_time >= 0 && i.array_buffer == array_buffer && i.morph_array_buffer == morph_array_buffer - && i.element_array_buffer == element_array_buffer && i.vertex_format == vertex_format - && i.size_vertex == size_vertex && i.compressed == compressed - && !memcmp(i.vertex_attrib_buffer_binding, - vertex_attrib_buffer_binding, sizeof(vertex_attrib_buffer_binding)) - && !memcmp(i.texcoord_array, texcoord_array, sizeof(texcoord_array))) - if (i.vertex_array) { - i.alive_time = 60; - return; - } - else { - vertex_array = &i; - break; - } + int32_t texcoord_array[2] = { -1, -1 }; + int32_t color_tex_index = 0; + for (const obj_material_texture_data& i : material->material.texdata) { + if (i.tex_index == -1) + continue; + + int32_t texcoord_index = obj_material_texture_type_get_texcoord_index( + i.shader_info.m.tex_type, color_tex_index); + if (texcoord_index < 0) + continue; + + texcoord_array[texcoord_index] = sub_mesh->uv_index[&i - material->material.texdata]; + + if (i.shader_info.m.tex_type == OBJ_MATERIAL_TEXTURE_COLOR) + color_tex_index++; + } + + GLuint vertex_attrib_buffer_binding[16] = {}; + object_data_get_vertex_attrib_buffer_bindings(args, + texcoord_array, vertex_attrib_buffer_binding); + + bool compressed = mesh->attrib.m.compressed; + GLsizei size_vertex = (GLsizei)mesh->size_vertex; + obj_vertex_format vertex_format = mesh->vertex_format; + + DispManager::vertex_array* vertex_array = 0; + for (DispManager::vertex_array& i : vertex_array_cache) + if (i.alive_time >= 0 && i.array_buffer == array_buffer && i.morph_array_buffer == morph_array_buffer + && i.element_array_buffer == element_array_buffer && i.vertex_format == vertex_format + && i.size_vertex == size_vertex && i.compressed == compressed + && !memcmp(i.vertex_attrib_buffer_binding, + vertex_attrib_buffer_binding, sizeof(vertex_attrib_buffer_binding)) + && !memcmp(i.texcoord_array, texcoord_array, sizeof(texcoord_array))) + if (i.vertex_array) { + i.alive_time = 60; + return; + } + else { + vertex_array = &i; + break; + } - if (!vertex_array) - for (object_data_vertex_array& i : vertex_array_cache) - if (i.alive_time <= 0) { - vertex_array = &i; - break; - } + if (!vertex_array) + for (DispManager::vertex_array& i : vertex_array_cache) + if (i.alive_time <= 0) { + vertex_array = &i; + break; + } - if (!vertex_array) { - vertex_array_cache.push_back({}); - vertex_array = &vertex_array_cache.back(); - } + if (!vertex_array) { + vertex_array_cache.push_back({}); + vertex_array = &vertex_array_cache.back(); + } - if (!vertex_array->vertex_array) { - glGenVertexArrays(1, &vertex_array->vertex_array); + if (!vertex_array->vertex_array) { + glGenVertexArrays(1, &vertex_array->vertex_array); + + gl_state_bind_vertex_array(vertex_array->vertex_array); + glVertexAttrib4f( POSITION_INDEX, 0.0f, 0.0f, 0.0f, 1.0f); + glVertexAttrib4f( BONE_WEIGHT_INDEX, 0.0f, 0.0f, 0.0f, 0.0f); + glVertexAttrib4f( NORMAL_INDEX, 0.0f, 0.0f, 0.0f, 1.0f); + glVertexAttrib4f( COLOR0_INDEX, 1.0f, 1.0f, 1.0f, 1.0f); + glVertexAttrib4f( COLOR1_INDEX, 1.0f, 1.0f, 1.0f, 1.0f); + glVertexAttrib4f( MORPH_COLOR_INDEX, 1.0f, 1.0f, 1.0f, 1.0f); + glVertexAttrib4f( TANGENT_INDEX, 0.0f, 0.0f, 0.0f, 1.0f); + glVertexAttrib4f( UNKNOWN_INDEX, 0.0f, 0.0f, 0.0f, 1.0f); + glVertexAttrib4f( TEXCOORD0_INDEX, 0.0f, 0.0f, 0.0f, 1.0f); + glVertexAttrib4f( TEXCOORD1_INDEX, 0.0f, 0.0f, 0.0f, 1.0f); + glVertexAttrib4f( MORPH_POSITION_INDEX, 0.0f, 0.0f, 0.0f, 1.0f); + glVertexAttrib4f( MORPH_NORMAL_INDEX, 0.0f, 0.0f, 0.0f, 1.0f); + glVertexAttrib4f( MORPH_TANGENT_INDEX, 0.0f, 0.0f, 0.0f, 1.0f); + glVertexAttrib4f(MORPH_TEXCOORD0_INDEX, 0.0f, 0.0f, 0.0f, 1.0f); + glVertexAttrib4f(MORPH_TEXCOORD1_INDEX, 0.0f, 0.0f, 0.0f, 1.0f); + glVertexAttrib4f( BONE_INDEX_INDEX, 0.0f, 0.0f, 0.0f, 0.0f); + } + + vertex_array->array_buffer = array_buffer; + vertex_array->morph_array_buffer = morph_array_buffer; + vertex_array->element_array_buffer = element_array_buffer; + vertex_array->alive_time = 60; + vertex_array->vertex_format = vertex_format; + vertex_array->size_vertex = size_vertex; + vertex_array->compressed = compressed; + memcpy(&vertex_array->texcoord_array, texcoord_array, sizeof(texcoord_array)); + memcpy(&vertex_array->vertex_attrib_buffer_binding, + vertex_attrib_buffer_binding, sizeof(vertex_attrib_buffer_binding)); gl_state_bind_vertex_array(vertex_array->vertex_array); - glVertexAttrib4f( POSITION_INDEX, 0.0f, 0.0f, 0.0f, 1.0f); - glVertexAttrib4f( BONE_WEIGHT_INDEX, 0.0f, 0.0f, 0.0f, 0.0f); - glVertexAttrib4f( NORMAL_INDEX, 0.0f, 0.0f, 0.0f, 1.0f); - glVertexAttrib4f( COLOR0_INDEX, 1.0f, 1.0f, 1.0f, 1.0f); - glVertexAttrib4f( COLOR1_INDEX, 1.0f, 1.0f, 1.0f, 1.0f); - glVertexAttrib4f( MORPH_COLOR_INDEX, 1.0f, 1.0f, 1.0f, 1.0f); - glVertexAttrib4f( TANGENT_INDEX, 0.0f, 0.0f, 0.0f, 1.0f); - glVertexAttrib4f( UNKNOWN_INDEX, 0.0f, 0.0f, 0.0f, 1.0f); - glVertexAttrib4f( TEXCOORD0_INDEX, 0.0f, 0.0f, 0.0f, 1.0f); - glVertexAttrib4f( TEXCOORD1_INDEX, 0.0f, 0.0f, 0.0f, 1.0f); - glVertexAttrib4f( MORPH_POSITION_INDEX, 0.0f, 0.0f, 0.0f, 1.0f); - glVertexAttrib4f( MORPH_NORMAL_INDEX, 0.0f, 0.0f, 0.0f, 1.0f); - glVertexAttrib4f( MORPH_TANGENT_INDEX, 0.0f, 0.0f, 0.0f, 1.0f); - glVertexAttrib4f(MORPH_TEXCOORD0_INDEX, 0.0f, 0.0f, 0.0f, 1.0f); - glVertexAttrib4f(MORPH_TEXCOORD1_INDEX, 0.0f, 0.0f, 0.0f, 1.0f); - glVertexAttrib4f( BONE_INDEX_INDEX, 0.0f, 0.0f, 0.0f, 0.0f); - } + gl_state_bind_array_buffer(array_buffer); + if (element_array_buffer) + gl_state_bind_element_array_buffer(element_array_buffer, true); - vertex_array->array_buffer = array_buffer; - vertex_array->morph_array_buffer = morph_array_buffer; - vertex_array->element_array_buffer = element_array_buffer; - vertex_array->alive_time = 60; - vertex_array->vertex_format = vertex_format; - vertex_array->size_vertex = size_vertex; - vertex_array->compressed = compressed; - memcpy(&vertex_array->texcoord_array, texcoord_array, sizeof(texcoord_array)); - memcpy(&vertex_array->vertex_attrib_buffer_binding, - vertex_attrib_buffer_binding, sizeof(vertex_attrib_buffer_binding)); - - gl_state_bind_vertex_array(vertex_array->vertex_array); - gl_state_bind_array_buffer(array_buffer); - if (element_array_buffer) - gl_state_bind_element_array_buffer(element_array_buffer, true); - - size_t offset = 0; - if (vertex_format & OBJ_VERTEX_POSITION) { - if (!vertex_array->vertex_attrib_array[POSITION_INDEX]) { - glEnableVertexAttribArray(POSITION_INDEX); - vertex_array->vertex_attrib_array[POSITION_INDEX] = true; - } - - glVertexAttribPointer(POSITION_INDEX, 3, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); - offset += 12; - } - else if (vertex_array->vertex_attrib_array[POSITION_INDEX]) { - glDisableVertexAttribArray(POSITION_INDEX); - vertex_array->vertex_attrib_array[POSITION_INDEX] = false; - } - - if (vertex_format & OBJ_VERTEX_NORMAL) { - if (!vertex_array->vertex_attrib_array[NORMAL_INDEX]) { - glEnableVertexAttribArray(NORMAL_INDEX); - vertex_array->vertex_attrib_array[NORMAL_INDEX] = true; - } - - if (!compressed) { - glVertexAttribPointer(NORMAL_INDEX, 3, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); - offset += 12; - } - else { - glVertexAttribPointer(NORMAL_INDEX, 3, GL_SHORT, GL_TRUE, size_vertex, (void*)offset); - offset += 8; - } - } - else if (vertex_array->vertex_attrib_array[NORMAL_INDEX]) { - glDisableVertexAttribArray(NORMAL_INDEX); - vertex_array->vertex_attrib_array[NORMAL_INDEX] = false; - } - - if (vertex_format & OBJ_VERTEX_TANGENT) { - if (!vertex_array->vertex_attrib_array[TANGENT_INDEX]) { - glEnableVertexAttribArray(TANGENT_INDEX); - vertex_array->vertex_attrib_array[TANGENT_INDEX] = true; - } - - if (!compressed) { - glVertexAttribPointer(TANGENT_INDEX, 4, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); - offset += 16; - } - else { - glVertexAttribPointer(TANGENT_INDEX, 4, GL_SHORT, GL_TRUE, size_vertex, (void*)offset); - offset += 8; - } - } - else if (vertex_array->vertex_attrib_array[TANGENT_INDEX]) { - glDisableVertexAttribArray(TANGENT_INDEX); - vertex_array->vertex_attrib_array[TANGENT_INDEX] = false; - } - - if (!compressed && vertex_format & OBJ_VERTEX_BINORMAL) - offset += 12; - - for (int32_t i = 0; i < 2; i++) { - int32_t texcoord_index = texcoord_array[i]; - if (texcoord_index < 0) { - if (vertex_array->vertex_attrib_array[TEXCOORD0_INDEX + i]) { - glDisableVertexAttribArray(TEXCOORD0_INDEX + i); - vertex_array->vertex_attrib_array[TEXCOORD0_INDEX + i] = false; - } - continue; - } - - if (vertex_format & (OBJ_VERTEX_TEXCOORD0 << texcoord_index)) { - if (!vertex_array->vertex_attrib_array[TEXCOORD0_INDEX + i]) { - glEnableVertexAttribArray(TEXCOORD0_INDEX + i); - vertex_array->vertex_attrib_array[TEXCOORD0_INDEX + i] = true; - } - - if (!compressed) - glVertexAttribPointer(TEXCOORD0_INDEX + i, 2, GL_FLOAT, GL_FALSE, - size_vertex, (void*)(offset + 8ULL * texcoord_index)); - else - glVertexAttribPointer(TEXCOORD0_INDEX + i, 2, GL_HALF_FLOAT, GL_FALSE, - size_vertex, (void*)(offset + 4ULL * texcoord_index)); - } - } - - if (!compressed) { - if (vertex_format & OBJ_VERTEX_TEXCOORD0) - offset += 8; - if (vertex_format & OBJ_VERTEX_TEXCOORD1) - offset += 8; - if (vertex_format & OBJ_VERTEX_TEXCOORD2) - offset += 8; - if (vertex_format & OBJ_VERTEX_TEXCOORD3) - offset += 8; - } - else { - if (vertex_format & OBJ_VERTEX_TEXCOORD0) - offset += 4; - if (vertex_format & OBJ_VERTEX_TEXCOORD1) - offset += 4; - if (vertex_format & OBJ_VERTEX_TEXCOORD2) - offset += 4; - if (vertex_format & OBJ_VERTEX_TEXCOORD3) - offset += 4; - } - - if (vertex_format & OBJ_VERTEX_COLOR0) { - if (!vertex_array->vertex_attrib_array[COLOR0_INDEX]) { - glEnableVertexAttribArray(COLOR0_INDEX); - vertex_array->vertex_attrib_array[COLOR0_INDEX] = true; - } - - if (!compressed) { - glVertexAttribPointer(COLOR0_INDEX, 4, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); - offset += 16; - } - else { - glVertexAttribPointer(COLOR0_INDEX, 4, GL_HALF_FLOAT, GL_FALSE, size_vertex, (void*)offset); - offset += 8; - } - } - else if (vertex_array->vertex_attrib_array[COLOR0_INDEX]) { - glDisableVertexAttribArray(COLOR0_INDEX); - vertex_array->vertex_attrib_array[COLOR0_INDEX] = false; - } - - if (vertex_format & OBJ_VERTEX_COLOR1) - offset += 16; - - if (vertex_format & OBJ_VERTEX_BONE_DATA) { - if (!vertex_array->vertex_attrib_array[BONE_WEIGHT_INDEX]) { - glEnableVertexAttribArray(BONE_WEIGHT_INDEX); - vertex_array->vertex_attrib_array[BONE_WEIGHT_INDEX] = true; - } - - if (!compressed) { - glVertexAttribPointer(BONE_WEIGHT_INDEX, 4, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); - offset += 16; - } - else { - glVertexAttribPointer(BONE_WEIGHT_INDEX, 4, GL_UNSIGNED_SHORT, GL_TRUE, size_vertex, (void*)offset); - offset += 8; - } - - if (!vertex_array->vertex_attrib_array[BONE_INDEX_INDEX]) { - glEnableVertexAttribArray(BONE_INDEX_INDEX); - vertex_array->vertex_attrib_array[BONE_INDEX_INDEX] = true; - } - - if (!compressed) { - glVertexAttribPointer(BONE_INDEX_INDEX, 4, GL_INT, GL_FALSE, size_vertex, (void*)offset); - offset += 16; - } - else { - glVertexAttribPointer(BONE_INDEX_INDEX, 4, GL_UNSIGNED_SHORT, GL_FALSE, size_vertex, (void*)offset); - offset += 8; - } - } - else { - if (vertex_array->vertex_attrib_array[BONE_WEIGHT_INDEX]) { - glDisableVertexAttribArray(BONE_WEIGHT_INDEX); - vertex_array->vertex_attrib_array[BONE_WEIGHT_INDEX] = false; - } - - if (vertex_array->vertex_attrib_array[BONE_INDEX_INDEX]) { - glDisableVertexAttribArray(BONE_INDEX_INDEX); - vertex_array->vertex_attrib_array[BONE_INDEX_INDEX] = false; - } - } - - if (!compressed && vertex_format & OBJ_VERTEX_UNKNOWN) { - if (!vertex_array->vertex_attrib_array[UNKNOWN_INDEX]) { - glEnableVertexAttribArray(UNKNOWN_INDEX); - vertex_array->vertex_attrib_array[UNKNOWN_INDEX] = true; - } - - glVertexAttribPointer(UNKNOWN_INDEX, 4, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); - offset += 16; - } - else if (vertex_array->vertex_attrib_array[UNKNOWN_INDEX]) { - glDisableVertexAttribArray(UNKNOWN_INDEX); - vertex_array->vertex_attrib_array[UNKNOWN_INDEX] = false; - } - - if (morph_array_buffer) { - gl_state_bind_array_buffer(morph_array_buffer); - - offset = 0; + size_t offset = 0; if (vertex_format & OBJ_VERTEX_POSITION) { - if (!vertex_array->vertex_attrib_array[MORPH_POSITION_INDEX]) { - glEnableVertexAttribArray(MORPH_POSITION_INDEX); - vertex_array->vertex_attrib_array[MORPH_POSITION_INDEX] = true; + if (!vertex_array->vertex_attrib_array[POSITION_INDEX]) { + glEnableVertexAttribArray(POSITION_INDEX); + vertex_array->vertex_attrib_array[POSITION_INDEX] = true; } - glVertexAttribPointer(MORPH_POSITION_INDEX, 3, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); + glVertexAttribPointer(POSITION_INDEX, + 3, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); offset += 12; } - else if (vertex_array->vertex_attrib_array[MORPH_POSITION_INDEX]) { - glDisableVertexAttribArray(MORPH_POSITION_INDEX); - vertex_array->vertex_attrib_array[MORPH_POSITION_INDEX] = false; + else if (vertex_array->vertex_attrib_array[POSITION_INDEX]) { + glDisableVertexAttribArray(POSITION_INDEX); + vertex_array->vertex_attrib_array[POSITION_INDEX] = false; } if (vertex_format & OBJ_VERTEX_NORMAL) { - if (!vertex_array->vertex_attrib_array[MORPH_NORMAL_INDEX]) { - glEnableVertexAttribArray(MORPH_NORMAL_INDEX); - vertex_array->vertex_attrib_array[MORPH_NORMAL_INDEX] = true; + if (!vertex_array->vertex_attrib_array[NORMAL_INDEX]) { + glEnableVertexAttribArray(NORMAL_INDEX); + vertex_array->vertex_attrib_array[NORMAL_INDEX] = true; } if (!compressed) { - glVertexAttribPointer(MORPH_NORMAL_INDEX, 3, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); + glVertexAttribPointer(NORMAL_INDEX, + 3, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); offset += 12; } else { - glVertexAttribPointer(MORPH_NORMAL_INDEX, 3, GL_SHORT, GL_TRUE, size_vertex, (void*)offset); + glVertexAttribPointer(NORMAL_INDEX, + 3, GL_SHORT, GL_TRUE, size_vertex, (void*)offset); offset += 8; } } - else if (vertex_array->vertex_attrib_array[MORPH_NORMAL_INDEX]) { - glDisableVertexAttribArray(MORPH_NORMAL_INDEX); - vertex_array->vertex_attrib_array[MORPH_NORMAL_INDEX] = false; + else if (vertex_array->vertex_attrib_array[NORMAL_INDEX]) { + glDisableVertexAttribArray(NORMAL_INDEX); + vertex_array->vertex_attrib_array[NORMAL_INDEX] = false; } if (vertex_format & OBJ_VERTEX_TANGENT) { - if (!vertex_array->vertex_attrib_array[MORPH_TANGENT_INDEX]) { - glEnableVertexAttribArray(MORPH_TANGENT_INDEX); - vertex_array->vertex_attrib_array[MORPH_TANGENT_INDEX] = true; + if (!vertex_array->vertex_attrib_array[TANGENT_INDEX]) { + glEnableVertexAttribArray(TANGENT_INDEX); + vertex_array->vertex_attrib_array[TANGENT_INDEX] = true; } if (!compressed) { - glVertexAttribPointer(MORPH_TANGENT_INDEX, 4, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); + glVertexAttribPointer(TANGENT_INDEX, + 4, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); offset += 16; } else { - glVertexAttribPointer(MORPH_TANGENT_INDEX, 4, GL_SHORT, GL_TRUE, size_vertex, (void*)offset); + glVertexAttribPointer(TANGENT_INDEX, + 4, GL_SHORT, GL_TRUE, size_vertex, (void*)offset); offset += 8; } } - else if (vertex_array->vertex_attrib_array[MORPH_TANGENT_INDEX]) { - glDisableVertexAttribArray(MORPH_TANGENT_INDEX); - vertex_array->vertex_attrib_array[MORPH_TANGENT_INDEX] = false; + else if (vertex_array->vertex_attrib_array[TANGENT_INDEX]) { + glDisableVertexAttribArray(TANGENT_INDEX); + vertex_array->vertex_attrib_array[TANGENT_INDEX] = false; } if (!compressed && vertex_format & OBJ_VERTEX_BINORMAL) offset += 12; - if (vertex_format & OBJ_VERTEX_TEXCOORD0) { - if (!vertex_array->vertex_attrib_array[MORPH_TEXCOORD0_INDEX]) { - glEnableVertexAttribArray(MORPH_TEXCOORD0_INDEX); - vertex_array->vertex_attrib_array[MORPH_TEXCOORD0_INDEX] = true; + for (int32_t i = 0; i < 2; i++) { + int32_t texcoord_index = texcoord_array[i]; + if (texcoord_index < 0) { + if (vertex_array->vertex_attrib_array[TEXCOORD0_INDEX + i]) { + glDisableVertexAttribArray(TEXCOORD0_INDEX + i); + vertex_array->vertex_attrib_array[TEXCOORD0_INDEX + i] = false; + } + continue; } - if (!compressed) { - glVertexAttribPointer(MORPH_TEXCOORD0_INDEX, 2, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); - offset += 8; - } - else { - glVertexAttribPointer(MORPH_TEXCOORD0_INDEX, 2, GL_HALF_FLOAT, GL_FALSE, size_vertex, (void*)offset); - offset += 4; - } - } - else if (vertex_array->vertex_attrib_array[MORPH_TEXCOORD0_INDEX]) { - glDisableVertexAttribArray(MORPH_TEXCOORD0_INDEX); - vertex_array->vertex_attrib_array[MORPH_TEXCOORD0_INDEX] = false; - } + if (vertex_format & (OBJ_VERTEX_TEXCOORD0 << texcoord_index)) { + if (!vertex_array->vertex_attrib_array[TEXCOORD0_INDEX + i]) { + glEnableVertexAttribArray(TEXCOORD0_INDEX + i); + vertex_array->vertex_attrib_array[TEXCOORD0_INDEX + i] = true; + } - if (vertex_format & OBJ_VERTEX_TEXCOORD1) { - if (!vertex_array->vertex_attrib_array[MORPH_TEXCOORD1_INDEX]) { - glEnableVertexAttribArray(MORPH_TEXCOORD1_INDEX); - vertex_array->vertex_attrib_array[MORPH_TEXCOORD1_INDEX] = true; + if (!compressed) + glVertexAttribPointer(TEXCOORD0_INDEX + i, 2, GL_FLOAT, GL_FALSE, + size_vertex, (void*)(offset + 8ULL * texcoord_index)); + else + glVertexAttribPointer(TEXCOORD0_INDEX + i, 2, GL_HALF_FLOAT, GL_FALSE, + size_vertex, (void*)(offset + 4ULL * texcoord_index)); } - - if (!compressed) { - glVertexAttribPointer(MORPH_TEXCOORD1_INDEX, 2, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); - offset += 8; - } - else { - glVertexAttribPointer(MORPH_TEXCOORD1_INDEX, 2, GL_HALF_FLOAT, GL_FALSE, size_vertex, (void*)offset); - offset += 4; - } - } - else if (vertex_array->vertex_attrib_array[MORPH_TEXCOORD1_INDEX]) { - glDisableVertexAttribArray(MORPH_TEXCOORD1_INDEX); - vertex_array->vertex_attrib_array[MORPH_TEXCOORD1_INDEX] = false; } if (!compressed) { + if (vertex_format & OBJ_VERTEX_TEXCOORD0) + offset += 8; + if (vertex_format & OBJ_VERTEX_TEXCOORD1) + offset += 8; if (vertex_format & OBJ_VERTEX_TEXCOORD2) offset += 8; if (vertex_format & OBJ_VERTEX_TEXCOORD3) offset += 8; } else { + if (vertex_format & OBJ_VERTEX_TEXCOORD0) + offset += 4; + if (vertex_format & OBJ_VERTEX_TEXCOORD1) + offset += 4; if (vertex_format & OBJ_VERTEX_TEXCOORD2) offset += 4; if (vertex_format & OBJ_VERTEX_TEXCOORD3) @@ -905,309 +562,1857 @@ void object_data::add_vertex_array(draw_object* draw) { } if (vertex_format & OBJ_VERTEX_COLOR0) { - uniform_value[U_MORPH_COLOR] = 1; - - if (!vertex_array->vertex_attrib_array[MORPH_COLOR_INDEX]) { - glEnableVertexAttribArray(MORPH_COLOR_INDEX); - vertex_array->vertex_attrib_array[MORPH_COLOR_INDEX] = true; + if (!vertex_array->vertex_attrib_array[COLOR0_INDEX]) { + glEnableVertexAttribArray(COLOR0_INDEX); + vertex_array->vertex_attrib_array[COLOR0_INDEX] = true; } if (!compressed) { - glVertexAttribPointer(MORPH_COLOR_INDEX, 4, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); + glVertexAttribPointer(COLOR0_INDEX, + 4, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); offset += 16; } else { - glVertexAttribPointer(MORPH_COLOR_INDEX, 4, GL_HALF_FLOAT, GL_FALSE, size_vertex, (void*)offset); + glVertexAttribPointer(COLOR0_INDEX, + 4, GL_HALF_FLOAT, GL_FALSE, size_vertex, (void*)offset); offset += 8; } } - else if (vertex_array->vertex_attrib_array[MORPH_COLOR_INDEX]) { - glDisableVertexAttribArray(MORPH_COLOR_INDEX); - vertex_array->vertex_attrib_array[MORPH_COLOR_INDEX] = false; + else if (vertex_array->vertex_attrib_array[COLOR0_INDEX]) { + glDisableVertexAttribArray(COLOR0_INDEX); + vertex_array->vertex_attrib_array[COLOR0_INDEX] = false; } - if (!compressed) { - if (vertex_format & OBJ_VERTEX_COLOR1) - offset += 16; + if (vertex_format & OBJ_VERTEX_COLOR1) + offset += 16; - if (vertex_format & OBJ_VERTEX_BONE_DATA) - offset += 32; + if (vertex_format & OBJ_VERTEX_BONE_DATA) { + if (!vertex_array->vertex_attrib_array[BONE_WEIGHT_INDEX]) { + glEnableVertexAttribArray(BONE_WEIGHT_INDEX); + vertex_array->vertex_attrib_array[BONE_WEIGHT_INDEX] = true; + } - if (vertex_format & OBJ_VERTEX_UNKNOWN) + if (!compressed) { + glVertexAttribPointer(BONE_WEIGHT_INDEX, + 4, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); offset += 16; + } + else { + glVertexAttribPointer(BONE_WEIGHT_INDEX, + 4, GL_UNSIGNED_SHORT, GL_TRUE, size_vertex, (void*)offset); + offset += 8; + } + + if (!vertex_array->vertex_attrib_array[BONE_INDEX_INDEX]) { + glEnableVertexAttribArray(BONE_INDEX_INDEX); + vertex_array->vertex_attrib_array[BONE_INDEX_INDEX] = true; + } + + glVertexAttribIPointer(BONE_INDEX_INDEX, + 4, GL_SHORT, size_vertex, (void*)offset); + offset += 8; } else { - if (vertex_format & OBJ_VERTEX_BONE_DATA) - offset += 16; + if (vertex_array->vertex_attrib_array[BONE_WEIGHT_INDEX]) { + glDisableVertexAttribArray(BONE_WEIGHT_INDEX); + vertex_array->vertex_attrib_array[BONE_WEIGHT_INDEX] = false; + } + + if (vertex_array->vertex_attrib_array[BONE_INDEX_INDEX]) { + glDisableVertexAttribArray(BONE_INDEX_INDEX); + vertex_array->vertex_attrib_array[BONE_INDEX_INDEX] = false; + } } + + if (!compressed && vertex_format & OBJ_VERTEX_UNKNOWN) { + if (!vertex_array->vertex_attrib_array[UNKNOWN_INDEX]) { + glEnableVertexAttribArray(UNKNOWN_INDEX); + vertex_array->vertex_attrib_array[UNKNOWN_INDEX] = true; + } + + glVertexAttribPointer(UNKNOWN_INDEX, + 4, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); + offset += 16; + } + else if (vertex_array->vertex_attrib_array[UNKNOWN_INDEX]) { + glDisableVertexAttribArray(UNKNOWN_INDEX); + vertex_array->vertex_attrib_array[UNKNOWN_INDEX] = false; + } + + if (morph_array_buffer) { + gl_state_bind_array_buffer(morph_array_buffer); + + offset = 0; + if (vertex_format & OBJ_VERTEX_POSITION) { + if (!vertex_array->vertex_attrib_array[MORPH_POSITION_INDEX]) { + glEnableVertexAttribArray(MORPH_POSITION_INDEX); + vertex_array->vertex_attrib_array[MORPH_POSITION_INDEX] = true; + } + + glVertexAttribPointer(MORPH_POSITION_INDEX, + 3, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); + offset += 12; + } + else if (vertex_array->vertex_attrib_array[MORPH_POSITION_INDEX]) { + glDisableVertexAttribArray(MORPH_POSITION_INDEX); + vertex_array->vertex_attrib_array[MORPH_POSITION_INDEX] = false; + } + + if (vertex_format & OBJ_VERTEX_NORMAL) { + if (!vertex_array->vertex_attrib_array[MORPH_NORMAL_INDEX]) { + glEnableVertexAttribArray(MORPH_NORMAL_INDEX); + vertex_array->vertex_attrib_array[MORPH_NORMAL_INDEX] = true; + } + + if (!compressed) { + glVertexAttribPointer(MORPH_NORMAL_INDEX, + 3, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); + offset += 12; + } + else { + glVertexAttribPointer(MORPH_NORMAL_INDEX, + 3, GL_SHORT, GL_TRUE, size_vertex, (void*)offset); + offset += 8; + } + } + else if (vertex_array->vertex_attrib_array[MORPH_NORMAL_INDEX]) { + glDisableVertexAttribArray(MORPH_NORMAL_INDEX); + vertex_array->vertex_attrib_array[MORPH_NORMAL_INDEX] = false; + } + + if (vertex_format & OBJ_VERTEX_TANGENT) { + if (!vertex_array->vertex_attrib_array[MORPH_TANGENT_INDEX]) { + glEnableVertexAttribArray(MORPH_TANGENT_INDEX); + vertex_array->vertex_attrib_array[MORPH_TANGENT_INDEX] = true; + } + + if (!compressed) { + glVertexAttribPointer(MORPH_TANGENT_INDEX, + 4, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); + offset += 16; + } + else { + glVertexAttribPointer(MORPH_TANGENT_INDEX, + 4, GL_SHORT, GL_TRUE, size_vertex, (void*)offset); + offset += 8; + } + } + else if (vertex_array->vertex_attrib_array[MORPH_TANGENT_INDEX]) { + glDisableVertexAttribArray(MORPH_TANGENT_INDEX); + vertex_array->vertex_attrib_array[MORPH_TANGENT_INDEX] = false; + } + + if (!compressed && vertex_format & OBJ_VERTEX_BINORMAL) + offset += 12; + + if (vertex_format & OBJ_VERTEX_TEXCOORD0) { + if (!vertex_array->vertex_attrib_array[MORPH_TEXCOORD0_INDEX]) { + glEnableVertexAttribArray(MORPH_TEXCOORD0_INDEX); + vertex_array->vertex_attrib_array[MORPH_TEXCOORD0_INDEX] = true; + } + + if (!compressed) { + glVertexAttribPointer(MORPH_TEXCOORD0_INDEX, + 2, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); + offset += 8; + } + else { + glVertexAttribPointer(MORPH_TEXCOORD0_INDEX, + 2, GL_HALF_FLOAT, GL_FALSE, size_vertex, (void*)offset); + offset += 4; + } + } + else if (vertex_array->vertex_attrib_array[MORPH_TEXCOORD0_INDEX]) { + glDisableVertexAttribArray(MORPH_TEXCOORD0_INDEX); + vertex_array->vertex_attrib_array[MORPH_TEXCOORD0_INDEX] = false; + } + + if (vertex_format & OBJ_VERTEX_TEXCOORD1) { + if (!vertex_array->vertex_attrib_array[MORPH_TEXCOORD1_INDEX]) { + glEnableVertexAttribArray(MORPH_TEXCOORD1_INDEX); + vertex_array->vertex_attrib_array[MORPH_TEXCOORD1_INDEX] = true; + } + + if (!compressed) { + glVertexAttribPointer(MORPH_TEXCOORD1_INDEX, + 2, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); + offset += 8; + } + else { + glVertexAttribPointer(MORPH_TEXCOORD1_INDEX, + 2, GL_HALF_FLOAT, GL_FALSE, size_vertex, (void*)offset); + offset += 4; + } + } + else if (vertex_array->vertex_attrib_array[MORPH_TEXCOORD1_INDEX]) { + glDisableVertexAttribArray(MORPH_TEXCOORD1_INDEX); + vertex_array->vertex_attrib_array[MORPH_TEXCOORD1_INDEX] = false; + } + + if (!compressed) { + if (vertex_format & OBJ_VERTEX_TEXCOORD2) + offset += 8; + if (vertex_format & OBJ_VERTEX_TEXCOORD3) + offset += 8; + } + else { + if (vertex_format & OBJ_VERTEX_TEXCOORD2) + offset += 4; + if (vertex_format & OBJ_VERTEX_TEXCOORD3) + offset += 4; + } + + if (vertex_format & OBJ_VERTEX_COLOR0) { + uniform_value[U_MORPH_COLOR] = 1; + + if (!vertex_array->vertex_attrib_array[MORPH_COLOR_INDEX]) { + glEnableVertexAttribArray(MORPH_COLOR_INDEX); + vertex_array->vertex_attrib_array[MORPH_COLOR_INDEX] = true; + } + + if (!compressed) { + glVertexAttribPointer(MORPH_COLOR_INDEX, + 4, GL_FLOAT, GL_FALSE, size_vertex, (void*)offset); + offset += 16; + } + else { + glVertexAttribPointer(MORPH_COLOR_INDEX, + 4, GL_HALF_FLOAT, GL_FALSE, size_vertex, (void*)offset); + offset += 8; + } + } + else if (vertex_array->vertex_attrib_array[MORPH_COLOR_INDEX]) { + glDisableVertexAttribArray(MORPH_COLOR_INDEX); + vertex_array->vertex_attrib_array[MORPH_COLOR_INDEX] = false; + } + + if (!compressed) { + if (vertex_format & OBJ_VERTEX_COLOR1) + offset += 16; + + if (vertex_format & OBJ_VERTEX_BONE_DATA) + offset += 32; + + if (vertex_format & OBJ_VERTEX_UNKNOWN) + offset += 16; + } + else { + if (vertex_format & OBJ_VERTEX_BONE_DATA) + offset += 16; + } + } + else { + if (vertex_array->vertex_attrib_array[MORPH_POSITION_INDEX]) { + glDisableVertexAttribArray(MORPH_POSITION_INDEX); + vertex_array->vertex_attrib_array[MORPH_POSITION_INDEX] = false; + } + + if (vertex_array->vertex_attrib_array[MORPH_NORMAL_INDEX]) { + glDisableVertexAttribArray(MORPH_NORMAL_INDEX); + vertex_array->vertex_attrib_array[MORPH_NORMAL_INDEX] = false; + } + + if (vertex_array->vertex_attrib_array[MORPH_TANGENT_INDEX]) { + glDisableVertexAttribArray(MORPH_TANGENT_INDEX); + vertex_array->vertex_attrib_array[MORPH_TANGENT_INDEX] = false; + } + + if (vertex_array->vertex_attrib_array[MORPH_TEXCOORD0_INDEX]) { + glDisableVertexAttribArray(MORPH_TEXCOORD0_INDEX); + vertex_array->vertex_attrib_array[MORPH_TEXCOORD0_INDEX] = false; + } + + if (vertex_array->vertex_attrib_array[MORPH_TEXCOORD1_INDEX]) { + glDisableVertexAttribArray(MORPH_TEXCOORD1_INDEX); + vertex_array->vertex_attrib_array[MORPH_TEXCOORD1_INDEX] = false; + } + + if (vertex_array->vertex_attrib_array[MORPH_COLOR_INDEX]) { + glDisableVertexAttribArray(MORPH_COLOR_INDEX); + vertex_array->vertex_attrib_array[MORPH_COLOR_INDEX] = false; + } + } + + gl_state_bind_array_buffer(0); + gl_state_bind_vertex_array(0); + if (element_array_buffer) + gl_state_bind_element_array_buffer(0); } - else { - if (vertex_array->vertex_attrib_array[MORPH_POSITION_INDEX]) { - glDisableVertexAttribArray(MORPH_POSITION_INDEX); - vertex_array->vertex_attrib_array[MORPH_POSITION_INDEX] = false; + + ObjData* DispManager::alloc_data(ObjKind kind) { + if (!buff) + return 0; + + int32_t size = align_val(sizeof(ObjKind) + sizeof(mat4) + sizeof(float_t) * 2, 0x08); + switch (kind) { + case OBJ_KIND_NORMAL: + size += sizeof(ObjSubMeshArgs); + break; + case OBJ_KIND_ETC: + size += sizeof(EtcObj); + break; + case OBJ_KIND_USER: + size += sizeof(UserArgs); + break; + case OBJ_KIND_TRANSLUCENT: + size += sizeof(ObjTranslucentArgs); + break; + default: + return 0; } - if (vertex_array->vertex_attrib_array[MORPH_NORMAL_INDEX]) { - glDisableVertexAttribArray(MORPH_NORMAL_INDEX); - vertex_array->vertex_attrib_array[MORPH_NORMAL_INDEX] = false; + if (buff_offset + size > buff_size) + return 0; + + ObjData* data = (ObjData*)((size_t)buff + buff_offset); + buff_offset += size; + if (buff_max < buff_offset) + buff_max = buff_offset; + return data; + } + + mat4* DispManager::alloc_data(int32_t count) { + if (!buff) + return 0; + + int32_t size = sizeof(mat4) * count; + if (buff_offset + size > buff_size) + return 0; + + mat4* mats = (mat4*)((size_t)buff + buff_offset); + buff_offset += size; + if (buff_max < buff_offset) + buff_max = buff_offset; + return mats; + } + + void DispManager::buffer_reset() { + buff_offset = 0; + } + + void DispManager::check_vertex_arrays() { + for (DispManager::vertex_array& i : vertex_array_cache) + if (i.alive_time > 0 && --i.alive_time <= 0) { + i.array_buffer = 0; + i.morph_array_buffer = 0; + } + } + + void DispManager::draw(ObjType type, int32_t depth_mask, bool a4) { + if (get_obj_count(type) < 1) + return; + + int32_t alpha_test = 0; + float_t min_alpha = 1.0f; + bool reflect = uniform_value[U_REFLECT] == 1; + void(*func)(render_context * rctx, const ObjSubMeshArgs * args) = draw_sub_mesh_default; + + for (int32_t i = 0; i < 6; i++) + gl_state_active_bind_texture_2d(i, 0); + gl_state_active_texture(0); + gl_state_set_blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + uniform_value_reset(); + gl_state_get(); + + rctx_ptr->obj_scene_ubo.Bind(0); + rctx_ptr->obj_batch_ubo.Bind(1); + rctx_ptr->obj_skinning_ubo.Bind(3); + + switch (type) { + case OBJ_TYPE_TRANSLUCENT: + case OBJ_TYPE_TRANSLUCENT_NO_SHADOW: + case OBJ_TYPE_TRANSLUCENT_LOCAL: + if (depth_mask) + func = draw_sub_mesh_translucent; + else + gl_state_set_depth_mask(GL_FALSE); + min_alpha = 0.0f; + break; + case OBJ_TYPE_TRANSPARENT: + case OBJ_TYPE_TRANSPARENT_LOCAL: + alpha_test = 1; + min_alpha = 0.1f; + break; + case OBJ_TYPE_SHADOW_CHARA: + case OBJ_TYPE_SHADOW_STAGE: + case OBJ_TYPE_SHADOW_OBJECT_CHARA: + case OBJ_TYPE_SHADOW_OBJECT_STAGE: + func = draw_sub_mesh_shadow; + min_alpha = 0.5f; + break; + case OBJ_TYPE_TYPE_6: + func = draw_sub_mesh_translucent; + gl_state_set_color_mask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + rctx_ptr->draw_state.shader_index = SHADER_FT_SIL; + break; + case OBJ_TYPE_TYPE_7: + func = draw_sub_mesh_translucent; + gl_state_set_color_mask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + rctx_ptr->draw_state.shader_index = SHADER_FT_SIL; + alpha_test = 1; + min_alpha = 0.0f; + break; + case OBJ_TYPE_REFLECT_CHARA_OPAQUE: + gl_state_set_cull_face_mode(GL_FRONT); + if (reflect) + func = draw_sub_mesh_reflect; + else if (rctx_ptr->render_manager.reflect_type == STAGE_DATA_REFLECT_REFLECT_MAP) + func = draw_sub_mesh_reflect_reflect_map; + break; + case OBJ_TYPE_REFLECT_CHARA_TRANSLUCENT: + gl_state_set_cull_face_mode(GL_FRONT); + if (reflect) + func = draw_sub_mesh_reflect; + else if (rctx_ptr->render_manager.reflect_type == STAGE_DATA_REFLECT_REFLECT_MAP) + func = draw_sub_mesh_reflect_reflect_map; + min_alpha = 0.0f; + break; + case OBJ_TYPE_REFLECT_CHARA_TRANSPARENT: + gl_state_set_cull_face_mode(GL_FRONT); + if (reflect) + func = draw_sub_mesh_reflect; + else if (rctx_ptr->render_manager.reflect_type == STAGE_DATA_REFLECT_REFLECT_MAP) + func = draw_sub_mesh_reflect_reflect_map; + alpha_test = 1; + min_alpha = 0.1f; + break; + case OBJ_TYPE_REFLECT_OPAQUE: + alpha_test = 1; + if (!a4) + func = draw_sub_mesh_reflect_reflect_map; + break; + case OBJ_TYPE_REFLECT_TRANSLUCENT: + case OBJ_TYPE_REFRACT_TRANSLUCENT: + gl_state_set_depth_mask(GL_FALSE); + min_alpha = 0.0f; + break; + case OBJ_TYPE_REFLECT_TRANSPARENT: + case OBJ_TYPE_REFRACT_TRANSPARENT: + alpha_test = 1; + min_alpha = 0.1f; + break; + case OBJ_TYPE_SSS: + func = draw_sub_mesh_sss; + break; + case OBJ_TYPE_USER: + func = draw_sub_mesh_translucent; + break; + default: + break; + } + rctx_ptr->obj_batch.g_max_alpha = { 0.0f, 0.0f, 0.0f, min_alpha }; + uniform_value[U_ALPHA_TEST] = alpha_test; + + for (ObjData*& i : obj[type]) { + switch (i->kind) { + case OBJ_KIND_NORMAL: { + draw_sub_mesh(rctx_ptr, &i->args.sub_mesh, &i->mat, func); + } break; + case OBJ_KIND_ETC: { + draw_object_model_mat_load(rctx_ptr, i->mat); + //draw_primitive_draw(&i->data.primitive); + } break; + case OBJ_KIND_USER: { + draw_object_model_mat_load(rctx_ptr, i->mat); + i->args.user.func(rctx_ptr, i->args.user.data); + } break; + case OBJ_KIND_TRANSLUCENT: { + for (uint32_t j = 0; j < i->args.translucent.count; j++) + draw_sub_mesh(rctx_ptr, i->args.translucent.sub_mesh[j], &i->mat, func); + } break; + } } - if (vertex_array->vertex_attrib_array[MORPH_TANGENT_INDEX]) { - glDisableVertexAttribArray(MORPH_TANGENT_INDEX); - vertex_array->vertex_attrib_array[MORPH_TANGENT_INDEX] = false; + switch (type) { + case OBJ_TYPE_TRANSLUCENT: + case OBJ_TYPE_TRANSLUCENT_NO_SHADOW: + case OBJ_TYPE_REFLECT_TRANSLUCENT: + case OBJ_TYPE_REFRACT_TRANSLUCENT: + if (!depth_mask) + gl_state_set_depth_mask(GL_TRUE); + break; + case OBJ_TYPE_TYPE_6: + case OBJ_TYPE_TYPE_7: + rctx_ptr->draw_state.shader_index = -1; + gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + break; + case OBJ_TYPE_REFLECT_CHARA_OPAQUE: + case OBJ_TYPE_REFLECT_CHARA_TRANSLUCENT: + case OBJ_TYPE_REFLECT_CHARA_TRANSPARENT: + gl_state_set_cull_face_mode(GL_BACK); + break; } - if (vertex_array->vertex_attrib_array[MORPH_TEXCOORD0_INDEX]) { - glDisableVertexAttribArray(MORPH_TEXCOORD0_INDEX); - vertex_array->vertex_attrib_array[MORPH_TEXCOORD0_INDEX] = false; + uniform_value_reset(); + shader_opengl::unbind(); + gl_state_set_blend_func(GL_ONE, GL_ZERO); + } + + void DispManager::draw_translucent(ObjType type, int32_t alpha) { + if (get_obj_count(type) < 1) + return; + + int32_t alpha_test = 0; + float_t min_alpha = 1.0f; + bool reflect = uniform_value[U_REFLECT] == 1; + void(*func)(render_context * rctx, const ObjSubMeshArgs * args) = draw_sub_mesh_default; + + for (int32_t i = 0; i < 6; i++) + gl_state_active_bind_texture_2d(i, 0); + gl_state_active_texture(0); + gl_state_set_blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + uniform_value_reset(); + gl_state_get(); + + switch (type) { + case OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_1: + case OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_2: + case OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_3: + case OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_2_LOCAL: + gl_state_set_depth_mask(GL_FALSE); + min_alpha = 0.0f; + break; + case OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_1: + case OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_2: + case OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_3: + case OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_2_LOCAL: + alpha_test = 1; + min_alpha = 0.1f; + break; + default: + break; + } + rctx_ptr->obj_batch.g_max_alpha = { 0.0f, 0.0f, 0.0f, min_alpha }; + uniform_value[U_ALPHA_TEST] = alpha_test; + + for (ObjData*& i : obj[type]) { + switch (i->kind) { + case OBJ_KIND_NORMAL: { + int32_t a = (int32_t)(i->args.sub_mesh.blend_color.w * 255.0f); + a = clamp_def(a, 0, 255); + if (a == alpha) { + draw_sub_mesh(rctx_ptr, &i->args.sub_mesh, &i->mat, func); + } + } break; + case OBJ_KIND_TRANSLUCENT: { + for (uint32_t j = 0; j < i->args.translucent.count; j++) { + ObjSubMeshArgs* args = i->args.translucent.sub_mesh[j]; + int32_t a = (int32_t)(args->blend_color.w * 255.0f); + a = clamp_def(a, 0, 255); + if (a == alpha) + draw_sub_mesh(rctx_ptr, args, &i->mat, func); + } + } break; + } } - if (vertex_array->vertex_attrib_array[MORPH_TEXCOORD1_INDEX]) { - glDisableVertexAttribArray(MORPH_TEXCOORD1_INDEX); - vertex_array->vertex_attrib_array[MORPH_TEXCOORD1_INDEX] = false; + switch (type) { + case OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_1: + case OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_2: + case OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_3: + gl_state_set_depth_mask(GL_TRUE); + break; + case OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_1: + case OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_2: + case OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_3: + gl_state_set_cull_face_mode(GL_BACK); + break; } - if (vertex_array->vertex_attrib_array[MORPH_COLOR_INDEX]) { - glDisableVertexAttribArray(MORPH_COLOR_INDEX); - vertex_array->vertex_attrib_array[MORPH_COLOR_INDEX] = false; + uniform_value_reset(); + shader_opengl::unbind(); + gl_state_set_blend_func(GL_ONE, GL_ZERO); + } + + /*void DispManager::draw_show_vector(ObjType type, int32_t show_vector) { + if (get_obj_count(type) < 1) + return; + + for (int32_t i = 0; i < 6; i++) + gl_state_active_bind_texture_2d(i, 0); + gl_state_active_texture(0); + gl_state_set_blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + uniform_value_reset(); + gl_state_get(); + + for (ObjData*& i : obj[type]) { + switch (i->type) { + case OBJ_KIND_NORMAL: { + draw_sub_mesh_show_vector(rctx_ptr, &i->args.sub_mesh, + &i->mat, show_vector); + } break; + case OBJ_KIND_TRANSLUCENT: { + for (uint32_t j = 0; j < i->args.translucent.count; j++) + draw_sub_mesh_show_vector(rctx_ptr, i->args.translucent.sub_mesh[j], + &i->mat, show_vector); + } break; + } + } + gl_state_set_blend_func(GL_ONE, GL_ZERO); + }*/ + + void DispManager::entry_list(ObjType type, ObjData* data) { + obj[type].push_back(data); + } + + static int32_t obj_axis_aligned_bounding_box_check_visibility( + obj_axis_aligned_bounding_box* aabb, camera* cam, const mat4* mat) { + vec3 points[8]; + points[0] = aabb->center + (aabb->size ^ vec3(0.0f, 0.0f, 0.0f)); + points[1] = aabb->center + (aabb->size ^ vec3(-0.0f, -0.0f, -0.0f)); + points[2] = aabb->center + (aabb->size ^ vec3(-0.0f, 0.0f, 0.0f)); + points[3] = aabb->center + (aabb->size ^ vec3(0.0f, -0.0f, -0.0f)); + points[4] = aabb->center + (aabb->size ^ vec3(0.0f, -0.0f, 0.0f)); + points[5] = aabb->center + (aabb->size ^ vec3(-0.0f, 0.0f, -0.0f)); + points[6] = aabb->center + (aabb->size ^ vec3(0.0f, 0.0f, -0.0f)); + points[7] = aabb->center + (aabb->size ^ vec3(-0.0f, -0.0f, 0.0f)); + + mat4 view_mat; + mat4_mult(mat, &cam->view, &view_mat); + for (int32_t i = 0; i < 8; i++) + mat4_mult_vec3_trans(&view_mat, &points[i], &points[i]); + + vec4 v2[6]; + *(vec3*)&v2[0] = { 0.0f, 0.0f, -1.0f }; + v2[0].w = (float_t)-cam->min_distance; + *(vec3*)&v2[1] = cam->field_1E4; + v2[1].w = 0.0f; + *(vec3*)&v2[2] = cam->field_1F0; + v2[2].w = 0.0f; + *(vec3*)&v2[3] = cam->field_1FC; + v2[3].w = 0.0f; + *(vec3*)&v2[4] = cam->field_208; + v2[4].w = 0.0f; + *(vec3*)&v2[5] = { 0.0f, 0.0f, 1.0f }; + v2[5].w = (float_t)cam->max_distance; + + for (int32_t i = 0; i < 6; i++) + for (int32_t j = 0; j < 8; j++) { + float_t v34 = vec3::dot(*(vec3*)&v2[i], points[j]) + v2[i].w; + if (v34 > 0.0f) + break; + + if (j == 7) + return 0; + } + return 1; + } + + static int32_t obj_bounding_sphere_check_visibility(obj_bounding_sphere* sphere, + CullingCheck* culling, camera* cam, const mat4* mat) { + if (culling->func) + return culling->func(sphere, &cam->view); + + vec3 center; + mat4_mult_vec3_trans(mat, &sphere->center, ¢er); + mat4_mult_vec3_trans(&cam->view, ¢er, ¢er); + float_t radius = mat4_get_max_scale(mat) * sphere->radius; + + double_t min_depth = (double_t)center.z - (double_t)radius; + double_t max_depth = (double_t)center.z + (double_t)radius; + if (-cam->min_distance < min_depth || -cam->max_distance > max_depth) + return 0; + + float_t v5 = vec3::dot(cam->field_1E4, center); + if (v5 < -radius) + return 0; + + float_t v6 = vec3::dot(cam->field_1F0, center); + if (v6 < -radius) + return 0; + + float_t v7 = vec3::dot(cam->field_1FC, center); + if (v7 < -radius) + return 0; + + float_t v8 = vec3::dot(cam->field_208, center); + if (v8 < -radius) + return 0; + + if (-cam->min_distance >= max_depth && -cam->max_distance <= min_depth + && v5 >= radius && v6 >= radius && v7 >= radius && v8 >= radius) + return 1; + return 2; + } + + bool DispManager::entry_obj(::obj* object, obj_mesh_vertex_buffer* obj_vertex_buf, + obj_mesh_index_buffer* obj_index_buf, const mat4* mat, + std::vector* textures, vec4* blend_color, mat4* bone_mat, ::obj* object_morph, + obj_mesh_vertex_buffer* obj_morph_vertex_buf, int32_t instances_count, + mat4* instances_mat, void(*func)(const ObjSubMeshArgs*), bool enable_bone_mat, bool local) { + ::camera* cam = rctx_ptr->camera; + cam->update_data(); + + if (!local && object_culling && !instances_count && !bone_mat && (!object + || !obj_bounding_sphere_check_visibility( + &object->bounding_sphere, &culling, cam, mat))) { + culling.culled.objects++; + return false; + } + culling.passed.objects++; + + for (uint32_t i = 0; i < object->num_mesh; i++) { + obj_mesh* mesh = &object->mesh_array[i]; + obj_mesh* mesh_morph = 0; + if (obj_vertex_buf && obj_morph_vertex_buf) { + if (obj_vertex_buf[i].get_size() != obj_morph_vertex_buf[i].get_size()) + continue; + + if (object_morph && i < object_morph->num_mesh) + mesh_morph = &object_morph->mesh_array[i]; + } + + if (!local && object_culling && !instances_count && !bone_mat + && !obj_bounding_sphere_check_visibility( + &mesh->bounding_sphere, &culling, cam, mat) + && (!mesh_morph || !obj_bounding_sphere_check_visibility( + &mesh_morph->bounding_sphere, &culling, cam, mat))) { + culling.culled.meshes++; + continue; + } + culling.passed.meshes++; + + ObjSubMeshArgs* translucent_priority[40]; + int32_t translucent_priority_count = 0; + + for (uint32_t j = 0; j < mesh->num_submesh; j++) { + obj_sub_mesh* sub_mesh = &mesh->submesh_array[j]; + obj_sub_mesh* sub_mesh_morph = 0; + if (sub_mesh->attrib.m.cloth) + continue; + + if (!local && object_culling && !instances_count && !bone_mat) { + int32_t v32 = obj_bounding_sphere_check_visibility( + &sub_mesh->bounding_sphere, &culling, cam, mat); + if (v32 != 2 || (!mesh->attrib.m.billboard && !mesh->attrib.m.billboard_y_axis)) { + if (v32 == 2) { + if (culling.func) + v32 = 1; + else + v32 = obj_axis_aligned_bounding_box_check_visibility( + &sub_mesh->axis_aligned_bounding_box, cam, mat); + } + + if (!v32) { + if (!mesh_morph || j >= mesh_morph->num_submesh) { + culling.culled.submesh_array++; + continue; + } + + sub_mesh_morph = &mesh_morph->submesh_array[j]; + if (!sub_mesh_morph) { + culling.culled.submesh_array++; + continue; + } + + v32 = obj_bounding_sphere_check_visibility( + &sub_mesh_morph->bounding_sphere, &culling, cam, mat); + if (v32 == 2) { + if (culling.func) + v32 = 1; + else + v32 = obj_axis_aligned_bounding_box_check_visibility( + &sub_mesh_morph->axis_aligned_bounding_box, cam, mat); + } + + if (!v32) { + culling.culled.submesh_array++; + continue; + } + } + } + } + culling.passed.submesh_array++; + + int32_t num_bone_index = sub_mesh->num_bone_index; + uint16_t* bone_index = sub_mesh->bone_index_array; + + mat4* mats; + if (num_bone_index && enable_bone_mat) { + mats = alloc_data(num_bone_index); + if (bone_mat) + for (int32_t k = 0; k < num_bone_index; k++, bone_index++) + mats[k] = bone_mat[*bone_index]; + else + for (int32_t k = 0; k < num_bone_index; k++) + mats[k] = mat4_identity; + } + else { + mats = 0; + num_bone_index = 0; + } + + obj_material_data* material = &object->material_array[sub_mesh->material_index]; + ObjData* data = alloc_data(OBJ_KIND_NORMAL); + if (!data) + continue; + + GLuint morph_array_buffer = 0; + if (obj_morph_vertex_buf) + morph_array_buffer = obj_morph_vertex_buf[i].get_buffer(); + + GLuint element_array_buffer = 0; + if (obj_index_buf) + element_array_buffer = obj_index_buf[i].buffer; + + GLuint array_buffer = 0; + if (obj_vertex_buf) + array_buffer = obj_vertex_buf[i].get_buffer(); + + uint32_t material_hash = hash_utf8_murmurhash(material->material.name); + + material_list_struct* mat_list = 0; + for (int32_t k = 0; k < material_list_count; k++) + if (material_list_array[k].hash == material_hash) { + mat_list = &material_list_array[k]; + break; + } + + vec4 _blend_color = 1.0f; + vec4 _emission = 0.0f; + + if (mat_list) { + bool has_blend_color = false; + if (mat_list->has_blend_color.x) { + _blend_color.x = mat_list->blend_color.x; + has_blend_color = true; + } + + if (mat_list->has_blend_color.y) { + _blend_color.y = mat_list->blend_color.y; + has_blend_color = true; + } + + if (mat_list->has_blend_color.z) { + _blend_color.z = mat_list->blend_color.z; + has_blend_color = true; + } + + if (mat_list->has_blend_color.w) { + _blend_color.w = mat_list->blend_color.w; + has_blend_color = true; + } + + if (blend_color) { + if (!has_blend_color) + _blend_color = *blend_color; + else + _blend_color *= *blend_color; + } + + bool has_emission = false; + if (mat_list->has_emission.x) { + _emission.x = mat_list->emission.x; + has_emission = true; + } + + if (mat_list->has_emission.y) { + _emission.y = mat_list->emission.y; + has_emission = true; + } + + if (mat_list->has_emission.z) { + _emission.z = mat_list->emission.z; + has_emission = true; + } + + if (mat_list->has_emission.w) { + _emission.w = mat_list->emission.w; + has_emission = true; + } + + if (!has_emission) + _emission = material->material.color.emission; + } + else { + if (blend_color) + _blend_color = *blend_color; + _emission = material->material.color.emission; + } + + data->init_sub_mesh(this, mat, object->bounding_sphere.radius, sub_mesh, + mesh, material, textures, num_bone_index, mats, array_buffer, element_array_buffer, + &_blend_color, &_emission, morph_array_buffer, instances_count, instances_mat, func); + + if (draw_task_flags & DRAW_TASK_SHADOW_OBJECT) { + entry_list((ObjType)(OBJ_TYPE_SHADOW_OBJECT_CHARA + + shadow_type), data); + if (draw_task_flags & DRAW_TASK_USER) + entry_list(OBJ_TYPE_USER, data); + continue; + } + + obj_material_attrib_member attrib = material->material.attrib.m; + if (draw_task_flags & (DRAW_TASK_ALPHA_ORDER_1 | DRAW_TASK_ALPHA_ORDER_2 | DRAW_TASK_ALPHA_ORDER_3) + && data->args.sub_mesh.blend_color.w < 1.0f) { + if (!(draw_task_flags & DRAW_TASK_NO_TRANSLUCENCY)) { + if (attrib.flag_28 || (attrib.punch_through + || !(attrib.alpha_texture | attrib.alpha_material)) + && !sub_mesh->attrib.m.transparent) { + if (!attrib.punch_through) { + if (draw_task_flags & DRAW_TASK_ALPHA_ORDER_1) + entry_list(OBJ_TYPE_OPAQUE_ALPHA_ORDER_1, data); + else if (draw_task_flags & DRAW_TASK_ALPHA_ORDER_2) + entry_list(local ? OBJ_TYPE_OPAQUE_ALPHA_ORDER_2_LOCAL + : OBJ_TYPE_OPAQUE_ALPHA_ORDER_2, data); + else + entry_list(OBJ_TYPE_OPAQUE_ALPHA_ORDER_3, data); + } + else { + if (draw_task_flags & DRAW_TASK_ALPHA_ORDER_1) + entry_list(OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_1, data); + else if (draw_task_flags & DRAW_TASK_ALPHA_ORDER_2) + entry_list(local ? OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_2_LOCAL + : OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_2, data); + else + entry_list(OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_3, data); + } + + if (draw_task_flags & DRAW_TASK_SSS) + entry_list(OBJ_TYPE_SSS, data); + } + + if (draw_task_flags & DRAW_TASK_ALPHA_ORDER_1) + entry_list(OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_1, data); + else if (draw_task_flags & DRAW_TASK_ALPHA_ORDER_2) + entry_list(local ? OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_2_LOCAL + : OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_2, data); + else + entry_list(OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_3, data); + } + } + else { + if (attrib.flag_28 || data->args.sub_mesh.blend_color.w >= 1.0f + && (attrib.punch_through || !(attrib.alpha_texture | attrib.alpha_material)) + && !sub_mesh->attrib.m.transparent) { + if (draw_task_flags & DRAW_TASK_SHADOW) + entry_list((ObjType)(OBJ_TYPE_SHADOW_CHARA + shadow_type), data); + + if (draw_task_flags & DRAW_TASK_SSS) + entry_list(OBJ_TYPE_SSS, data); + + if (attrib.punch_through) { + if (!(draw_task_flags & DRAW_TASK_NO_TRANSLUCENCY)) + entry_list(local ? OBJ_TYPE_TRANSPARENT_LOCAL + : OBJ_TYPE_TRANSPARENT, data); + + if (draw_task_flags & DRAW_TASK_CHARA_REFLECT) + entry_list(OBJ_TYPE_REFLECT_CHARA_OPAQUE, data); + + if (draw_task_flags & DRAW_TASK_REFLECT) + entry_list(OBJ_TYPE_REFLECT_OPAQUE, data); + + if (draw_task_flags & DRAW_TASK_REFRACT) + entry_list(OBJ_TYPE_REFRACT_TRANSPARENT, data); + } + else { + if (!(draw_task_flags & DRAW_TASK_NO_TRANSLUCENCY)) + entry_list(local ? OBJ_TYPE_OPAQUE_LOCAL + : OBJ_TYPE_OPAQUE, data); + + if (draw_task_flags & DRAW_TASK_20) + entry_list(OBJ_TYPE_TYPE_6, data); + + if (draw_task_flags & DRAW_TASK_CHARA_REFLECT) + entry_list(OBJ_TYPE_REFLECT_CHARA_OPAQUE, data); + + if (draw_task_flags & DRAW_TASK_REFLECT) + entry_list(OBJ_TYPE_REFLECT_OPAQUE, data); + + if (draw_task_flags & DRAW_TASK_REFRACT) + entry_list(OBJ_TYPE_REFRACT_OPAQUE, data); + } + + if (draw_task_flags & DRAW_TASK_USER) + entry_list(OBJ_TYPE_USER, data); + continue; + } + else if (!(draw_task_flags & DRAW_TASK_NO_TRANSLUCENCY)) { + if (!attrib.translucent_priority) + if (local) + entry_list(OBJ_TYPE_TRANSLUCENT_LOCAL, data); + else if (mesh->attrib.m.translucent_no_shadow + || draw_task_flags & DRAW_TASK_TRANSLUCENT_NO_SHADOW) { + entry_list(OBJ_TYPE_TRANSLUCENT_NO_SHADOW, data); + } + else { + entry_list(OBJ_TYPE_TRANSLUCENT, data); + entry_list(OBJ_TYPE_TRANSLUCENT_LITPROJ_0, data); + entry_list(OBJ_TYPE_TRANSLUCENT_LITPROJ_1, data); + } + else if (translucent_priority_count < 40) + translucent_priority[translucent_priority_count++] = &data->args.sub_mesh; + } + } + + if (draw_task_flags & DRAW_TASK_SHADOW) + entry_list((ObjType)(OBJ_TYPE_SHADOW_CHARA + + shadow_type), data); + if (draw_task_flags & DRAW_TASK_40) + entry_list(OBJ_TYPE_TYPE_7, data); + if (draw_task_flags & DRAW_TASK_CHARA_REFLECT) + entry_list(OBJ_TYPE_REFLECT_CHARA_OPAQUE, data); + if (draw_task_flags & DRAW_TASK_REFLECT) { + if (rctx_ptr->render_manager.reflect_type != STAGE_DATA_REFLECT_REFLECT_MAP) + entry_list(OBJ_TYPE_REFLECT_OPAQUE, data); + else + entry_list(OBJ_TYPE_REFLECT_TRANSLUCENT, data); + } + if (draw_task_flags & DRAW_TASK_REFRACT) + entry_list(OBJ_TYPE_REFRACT_TRANSLUCENT, data); + if (draw_task_flags & DRAW_TASK_USER) + entry_list(OBJ_TYPE_USER, data); + } + + if (!translucent_priority_count) + continue; + + ObjTranslucentArgs translucent_args; + translucent_args.count = 0; + for (int32_t j = 62; j; j--) + for (int32_t k = 0; k < translucent_priority_count; k++) { + ObjSubMeshArgs* sub_mesh = translucent_priority[k]; + if (sub_mesh->material->material.attrib.m.translucent_priority != j) + continue; + + translucent_args.sub_mesh[translucent_args.count] = sub_mesh; + translucent_args.count++; + } + + ObjData* data = alloc_data(OBJ_KIND_TRANSLUCENT); + if (!data) + continue; + + data->init_translucent(mat, &translucent_args); + if (draw_task_flags & DRAW_TASK_ALPHA_ORDER_1) + entry_list(OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_1, data); + else if (draw_task_flags & DRAW_TASK_ALPHA_ORDER_2) + entry_list(local ? OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_2_LOCAL + : OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_2, data); + else if (draw_task_flags & DRAW_TASK_ALPHA_ORDER_3) + entry_list(OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_3, data); + else + entry_list(local ? OBJ_TYPE_TRANSLUCENT_LOCAL + : OBJ_TYPE_TRANSLUCENT, data); + } + return true; + } + + void DispManager::entry_obj_by_obj(const mat4* mat, + ::obj* obj, std::vector* textures, obj_mesh_vertex_buffer* obj_vert_buf, + obj_mesh_index_buffer* obj_index_buf, mat4* bone_mat, float_t alpha) { + if (!obj) + return; + + vec4 blend_color = { 1.0f, 1.0f, 1.0f, alpha }; + vec4* blend_color_ptr = alpha < 1.0f ? &blend_color : 0; + + entry_obj(obj, obj_vert_buf, obj_index_buf, mat, textures, blend_color_ptr, + bone_mat, 0, 0, 0, 0, 0, !!bone_mat); + } + + bool DispManager::entry_obj_by_object_info(const mat4* mat, object_info obj_info, mat4* bone_mat) { + vec4 blend_color = 1.0f; + return entry_obj_by_object_info(mat, obj_info, &blend_color, bone_mat, 0, 0, 0, true); + } + + bool DispManager::entry_obj_by_object_info(const mat4* mat, object_info obj_info, + vec4* blend_color, mat4* bone_mat, int32_t instances_count, + mat4* instances_mat, void(*func)(const ObjSubMeshArgs*), bool enable_bone_mat, bool local) { + if (obj_info.id == -1 && obj_info.set_id == -1) + return false; + + ::obj* object = object_storage_get_obj(obj_info); + if (!object) + return false; + + std::vector* textures = object_storage_get_obj_set_textures(obj_info.set_id); + obj_mesh_vertex_buffer* obj_vertex_buffer = object_storage_get_obj_mesh_vertex_buffer(obj_info); + obj_mesh_index_buffer* obj_index_buffer = object_storage_get_obj_mesh_index_buffer(obj_info); + + ::obj* obj_morph = 0; + obj_mesh_vertex_buffer* obj_morph_vertex_buffer = 0; + if (morph.object.set_id != -1) { + obj_morph = object_storage_get_obj(morph.object); + obj_morph_vertex_buffer = object_storage_get_obj_mesh_vertex_buffer(morph.object); + } + + return entry_obj(object, obj_vertex_buffer, obj_index_buffer, + mat, textures, blend_color, bone_mat, obj_morph, obj_morph_vertex_buffer, + instances_count, instances_mat, func, enable_bone_mat, local); + } + + bool DispManager::entry_obj_by_object_info(const mat4* mat, + object_info obj_info, float_t alpha, mat4* bone_mat) { + vec4 blend_color = 1.0f; + blend_color.w = alpha; + return entry_obj_by_object_info(mat, obj_info, &blend_color, bone_mat, 0, 0, 0, true); + } + + bool DispManager::entry_obj_by_object_info(const mat4* mat, object_info obj_info, + float_t r, float_t g, float_t b, float_t a, mat4* bone_mat, bool local) { + vec4 blend_color = { r, g, b, a }; + return entry_obj_by_object_info(mat, obj_info, &blend_color, bone_mat, 0, 0, 0, true, local); + } + + bool DispManager::entry_obj_by_object_info(const mat4* mat, object_info obj_info, + vec4* blend_color, mat4* bone_mat, bool local) { + return entry_obj_by_object_info(mat, obj_info, blend_color, 0, 0, 0, 0, false, local); + } + + void DispManager::entry_obj_by_object_info_object_skin(object_info obj_info, + std::vector* texture_pattern, texture_data_struct* texture_data, float_t alpha, + mat4* matrices, mat4* ex_data_matrices, const mat4* mat, const mat4* global_mat) { + obj_skin* skin = object_storage_get_obj_skin(obj_info); + if (!skin) + return; + + obj_skin_set_matrix_buffer(skin, matrices, ex_data_matrices, rctx_ptr->matrix_buffer, mat, global_mat); + + vec4 texture_color_coefficients; + vec4 texture_color_offset; + vec4 texture_specular_coefficients; + vec4 texture_specular_offset; + if (texture_data && !texture_data->field_0) { + vec4 value; + get_texture_color_coeff(texture_color_coefficients); + *(vec3*)&value = texture_data->texture_color_coefficients * *(vec3*)&texture_color_coefficients; + value.w = 0.0f; + set_texture_color_coefficients(value); + + get_texture_color_offset(texture_color_offset); + *(vec3*)&value = texture_data->texture_color_offset; + value.w = 0.0f; + set_texture_color_offset(value); + + get_texture_specular_coeff(texture_specular_coefficients); + *(vec3*)&value = texture_data->texture_specular_coefficients * *(vec3*)&texture_specular_coefficients; + value.w = 0.0f; + set_texture_specular_coefficients(value); + + get_texture_specular_offset(texture_specular_offset); + *(vec3*)&value = texture_data->texture_specular_offset; + value.w = 0.0f; + set_texture_specular_offset(value); + } + + size_t texture_pattern_count = texture_pattern ? texture_pattern->size() : 0; + if (texture_pattern && texture_pattern_count) + set_texture_pattern((int32_t)texture_pattern_count, texture_pattern->data()); + + if (fabsf(alpha - 1.0f) > 0.000001f) + entry_obj_by_object_info(global_mat, obj_info, alpha, rctx_ptr->matrix_buffer); + else + entry_obj_by_object_info(global_mat, obj_info, rctx_ptr->matrix_buffer); + + if (texture_pattern && texture_pattern_count) + set_texture_pattern(); + + if (texture_data && !texture_data->field_0) { + set_texture_color_coefficients(texture_color_coefficients); + set_texture_color_offset(texture_color_offset); + set_texture_specular_coefficients(texture_specular_coefficients); + set_texture_specular_offset(texture_specular_offset); } } - gl_state_bind_array_buffer(0); - gl_state_bind_vertex_array(0); - if (element_array_buffer) - gl_state_bind_element_array_buffer(0); -} - -void object_data::check_vertex_arrays() { - for (object_data_vertex_array& i : vertex_array_cache) - if (i.alive_time > 0 && --i.alive_time <= 0) { - i.array_buffer = 0; - i.morph_array_buffer = 0; + void DispManager::entry_obj_user(const mat4* mat, UserArgsFunc func, void* data, ObjType type) { + ObjData* _data = alloc_data(OBJ_KIND_USER); + if (_data) { + _data->init_user(mat, func, data); + entry_list(type, _data); } -} - -GLuint object_data::get_vertex_array(draw_object* draw) { - obj_mesh* mesh = draw->mesh; - obj_sub_mesh* sub_mesh = draw->sub_mesh; - obj_material_data* material = draw->material; - - GLuint array_buffer = draw->array_buffer; - GLuint morph_array_buffer = draw->morph_array_buffer; - GLuint element_array_buffer = draw->element_array_buffer; - - int32_t texcoord_array[2] = { -1, -1 }; - int32_t color_tex_index = 0; - for (obj_material_texture_data& i : material->material.texdata) { - if (i.tex_index == -1) - continue; - - int32_t texcoord_index = obj_material_texture_type_get_texcoord_index( - i.shader_info.m.tex_type, color_tex_index); - if (texcoord_index < 0) - continue; - - texcoord_array[texcoord_index] = sub_mesh->uv_index[&i - material->material.texdata]; - - if (i.shader_info.m.tex_type == OBJ_MATERIAL_TEXTURE_COLOR) - color_tex_index++; } - GLuint vertex_attrib_buffer_binding[16] = {}; - object_data_get_vertex_attrib_buffer_bindings(draw, - texcoord_array, vertex_attrib_buffer_binding); + GLuint DispManager::get_vertex_array(const ObjSubMeshArgs* args) { + const obj_mesh* mesh = args->mesh; + const obj_sub_mesh* sub_mesh = args->sub_mesh; + const obj_material_data* material = args->material; - bool compressed = mesh->attrib.m.compressed; - GLsizei size_vertex = (GLsizei)mesh->size_vertex; - obj_vertex_format vertex_format = mesh->vertex_format; + GLuint array_buffer = args->array_buffer; + GLuint morph_array_buffer = args->morph_array_buffer; + GLuint element_array_buffer = args->element_array_buffer; - object_data_vertex_array* vertex_array = 0; - for (object_data_vertex_array& i : vertex_array_cache) - if (i.alive_time > 0 && i.array_buffer == array_buffer && i.morph_array_buffer == morph_array_buffer - && i.element_array_buffer == element_array_buffer && i.vertex_format == vertex_format - && i.size_vertex == size_vertex && i.compressed == compressed - && !memcmp(i.vertex_attrib_buffer_binding, - vertex_attrib_buffer_binding, sizeof(vertex_attrib_buffer_binding)) - && !memcmp(i.texcoord_array, texcoord_array, sizeof(texcoord_array))) - return i.vertex_array; - return 0; -} + int32_t texcoord_array[2] = { -1, -1 }; + int32_t color_tex_index = 0; + for (const obj_material_texture_data& i : material->material.texdata) { + if (i.tex_index == -1) + continue; -bool object_data::get_chara_color() { - return chara_color; -} + int32_t texcoord_index = obj_material_texture_type_get_texcoord_index( + i.shader_info.m.tex_type, color_tex_index); + if (texcoord_index < 0) + continue; -::draw_task_flags object_data::get_draw_task_flags() { - return draw_task_flags; -} + texcoord_array[texcoord_index] = sub_mesh->uv_index[&i - material->material.texdata]; -void object_data::get_material_list(int32_t& count, material_list_struct*& value) { - count = material_list_count; + if (i.shader_info.m.tex_type == OBJ_MATERIAL_TEXTURE_COLOR) + color_tex_index++; + } - for (int32_t i = 0; i < count; i++) - value[i] = material_list_array[i]; -} + GLuint vertex_attrib_buffer_binding[16] = {}; + object_data_get_vertex_attrib_buffer_bindings(args, + texcoord_array, vertex_attrib_buffer_binding); -void object_data::get_morph(object_info* object, float_t* value) { - *value = morph.value; - *object = morph.object; -} + bool compressed = mesh->attrib.m.compressed; + GLsizei size_vertex = (GLsizei)mesh->size_vertex; + obj_vertex_format vertex_format = mesh->vertex_format; -shadow_type_enum object_data::get_shadow_type() { - return shadow_type; -} + DispManager::vertex_array* vertex_array = 0; + for (DispManager::vertex_array& i : vertex_array_cache) + if (i.alive_time > 0 && i.array_buffer == array_buffer && i.morph_array_buffer == morph_array_buffer + && i.element_array_buffer == element_array_buffer && i.vertex_format == vertex_format + && i.size_vertex == size_vertex && i.compressed == compressed + && !memcmp(i.vertex_attrib_buffer_binding, + vertex_attrib_buffer_binding, sizeof(vertex_attrib_buffer_binding)) + && !memcmp(i.texcoord_array, texcoord_array, sizeof(texcoord_array))) + return i.vertex_array; + return 0; + } -void object_data::get_texture_color_coeff(vec4& value) { - value = texture_color_coeff; -} + bool DispManager::get_chara_color() { + return chara_color; + } -void object_data::get_texture_color_offset(vec4& value) { - value = texture_color_offset; -} + ::draw_task_flags DispManager::get_draw_task_flags() { + return draw_task_flags; + } -void object_data::get_texture_pattern(int32_t& count, texture_pattern_struct*& value) { - count = texture_pattern_count; + void DispManager::get_material_list(int32_t& count, material_list_struct*& value) { + count = material_list_count; - for (int32_t i = 0; i < count; i++) - value[i] = texture_pattern_array[i]; -} - -void object_data::get_texture_specular_coeff(vec4& value) { - value = texture_specular_coeff; -} - -void object_data::get_texture_specular_offset(vec4& value) { - value = texture_specular_offset; -} - -void object_data::get_texture_transform(int32_t& count, texture_transform_struct*& value) { - count = texture_transform_count; - - for (int32_t i = 0; i < count; i++) - value[i] = texture_transform_array[i]; -} - -float_t object_data::get_wet_param() { - return wet_param; -} - -void object_data::reset() { - passed_prev = passed; - culled_prev = culled; - draw_task_flags = (::draw_task_flags)0; - field_8 = 0; - field_C = 0; - passed = {}; - culled = {}; - - texture_pattern_count = 0; - memset(texture_pattern_array, 0, sizeof(texture_pattern_array)); - texture_transform_count = 0; - memset(texture_transform_array, 0, sizeof(texture_transform_array)); - texture_color_coeff = 1.0f; - texture_color_offset = 0.0f; - texture_specular_coeff = 1.0f; - texture_specular_offset = 0.0f; - - for (std::vector& i : draw_task_array) - i.clear(); - buffer.reset(); -} - -void object_data::set_chara_color(bool value) { - chara_color = value; -} - -void object_data::set_draw_task_flags(::draw_task_flags flags) { - draw_task_flags = flags; -} - -void object_data::set_material_list(int32_t count, material_list_struct* value) { - if (count > MATERIAL_LIST_COUNT) - return; - - material_list_count = count; - - if (count) for (int32_t i = 0; i < count; i++) - material_list_array[i] = value[i]; - else - for (int32_t i = 0; i < MATERIAL_LIST_COUNT; i++) - material_list_array[i] = {}; -} + value[i] = material_list_array[i]; + } -void object_data::set_morph(object_info object, float_t value) { - morph.value = value; - morph.object = object; -} + void DispManager::get_morph(object_info& object, float_t& weight) { + weight = morph.weight; + object = morph.object; + } -void object_data::set_object_bounding_sphere_check_func( - bool(*func)(obj_bounding_sphere*, camera*)) { - object_bounding_sphere_check_func = func; -} + int32_t DispManager::get_obj_count(ObjType type) { + return (int32_t)obj[type].size(); + } -void object_data::set_shadow_type(shadow_type_enum type) { - if (type == SHADOW_CHARA || type == SHADOW_STAGE) - shadow_type = type; -} + shadow_type_enum DispManager::get_shadow_type() { + return shadow_type; + } -void object_data::set_texture_color_coeff(vec4& value) { - texture_color_coeff = value; -} + void DispManager::get_texture_color_coeff(vec4& value) { + value = texture_color_coefficients; + } -void object_data::set_texture_color_offset(vec4& value) { - texture_color_offset = value; -} + void DispManager::get_texture_color_offset(vec4& value) { + value = texture_color_offset; + } -void object_data::set_texture_pattern(int32_t count, texture_pattern_struct* value) { - if (count > TEXTURE_PATTERN_COUNT) - return; + void DispManager::get_texture_pattern(int32_t& count, texture_pattern_struct*& value) { + count = texture_pattern_count; - texture_pattern_count = count; - - if (count) for (int32_t i = 0; i < count; i++) - texture_pattern_array[i] = value[i]; - else - for (int32_t i = 0; i < TEXTURE_PATTERN_COUNT; i++) - texture_pattern_array[i] = {}; -} + value[i] = texture_pattern_array[i]; + } -void object_data::set_texture_specular_coeff(vec4& value) { - texture_specular_coeff = value; -} + void DispManager::get_texture_specular_coeff(vec4& value) { + value = texture_specular_coefficients; + } -void object_data::set_texture_specular_offset(vec4& value) { - texture_specular_offset = value; -} + void DispManager::get_texture_specular_offset(vec4& value) { + value = texture_specular_offset; + } -void object_data::set_texture_transform(int32_t count, texture_transform_struct* value) { - if (count > TEXTURE_TRANSFORM_COUNT) - return; + void DispManager::get_texture_transform(int32_t& count, texture_transform_struct*& value) { + count = texture_transform_count; - texture_transform_count = count; - - if (count) for (int32_t i = 0; i < count; i++) - texture_transform_array[i] = value[i]; - else - for (int32_t i = count; i < TEXTURE_TRANSFORM_COUNT; i++) - texture_transform_array[i] = {}; + value[i] = texture_transform_array[i]; + } + + float_t DispManager::get_wet_param() { + return wet_param; + } + + static int mdl_obj_data_sort_quicksort_compare0(void const* src1, void const* src2) { + float_t d1 = (*(ObjData**)src1)->view_z; + float_t d2 = (*(ObjData**)src2)->view_z; + return d1 > d2 ? -1 : (d1 < d2 ? 1 : 0); + } + + static int mdl_obj_data_sort_quicksort_compare1(void const* src1, void const* src2) { + float_t d1 = (*(ObjData**)src1)->view_z; + float_t d2 = (*(ObjData**)src2)->view_z; + return d1 < d2 ? -1 : (d1 > d2 ? 1 : 0); + } + + static int mdl_obj_data_sort_quicksort_compare2(void const* src1, void const* src2) { + float_t r1 = (*(ObjData**)src1)->radius; + float_t r2 = (*(ObjData**)src2)->radius; + return r1 > r2 ? -1 : (r1 < r2 ? 1 : 0); + } + + void DispManager::obj_sort(mat4* view, ObjType type, int32_t compare_func) { + std::vector& vec = obj[type]; + if (vec.size() < 1) + return; + + for (ObjData*& i : vec) { + vec3 center; + mat4_get_translation(&i->mat, ¢er); + if (i->kind == OBJ_KIND_NORMAL) { + mat4 mat = i->mat; + if (i->args.sub_mesh.mesh->attrib.m.billboard) + model_mat_face_camera_view(view, &mat, &mat); + else if (i->args.sub_mesh.mesh->attrib.m.billboard_y_axis) + model_mat_face_camera_position(view, &mat, &mat); + + const obj_sub_mesh* sub_mesh = i->args.sub_mesh.sub_mesh; + if (i->args.sub_mesh.mat_count < 1 || !sub_mesh->num_bone_index) + mat4_mult_vec3_trans(&mat, &sub_mesh->bounding_sphere.center, ¢er); + else { + vec3 center_sum = 0.0f; + for (uint32_t j = 0; j < sub_mesh->num_bone_index; j++) { + center = sub_mesh->bounding_sphere.center; + mat4_mult_vec3_trans(&i->args.sub_mesh.mats[j], ¢er, ¢er); + center_sum += center; + } + center_sum *= 1.0f / (float_t)sub_mesh->num_bone_index; + } + i->radius = i->args.sub_mesh.mesh->bounding_sphere.radius; + } + + mat4_mult_vec3_trans(view, ¢er, ¢er); + i->view_z = center.z; + } + + switch (compare_func) { + case 0: + quicksort_custom(vec.data(), vec.size(), + sizeof(ObjData*), mdl_obj_data_sort_quicksort_compare0); + break; + case 1: + quicksort_custom(vec.data(), vec.size(), + sizeof(ObjData*), mdl_obj_data_sort_quicksort_compare1); + break; + case 2: + quicksort_custom(vec.data(), vec.size(), + sizeof(ObjData*), mdl_obj_data_sort_quicksort_compare2); + break; + } + } + + void DispManager::refresh() { + culling.passed_prev = culling.passed; + culling.culled_prev = culling.culled; + culling.passed = {}; + culling.culled = {}; + + draw_task_flags = (::draw_task_flags)0; + field_8 = 0; + field_C = 0; + + texture_pattern_count = 0; + memset(texture_pattern_array, 0, sizeof(texture_pattern_array)); + texture_transform_count = 0; + memset(texture_transform_array, 0, sizeof(texture_transform_array)); + texture_color_coefficients = 1.0f; + texture_color_offset = 0.0f; + texture_specular_coefficients = 1.0f; + texture_specular_offset = 0.0f; + + for (std::vector& i : obj) + i.clear(); + + buffer_reset(); + } + + void DispManager::set_chara_color(bool value) { + chara_color = value; + } + + void DispManager::set_draw_task_flags(::draw_task_flags flags) { + draw_task_flags = flags; + } + + void DispManager::set_material_list(int32_t count, material_list_struct* value) { + if (count > MATERIAL_LIST_COUNT) + return; + + material_list_count = count; + + if (count) + for (int32_t i = 0; i < count; i++) + material_list_array[i] = value[i]; + else + for (int32_t i = 0; i < MATERIAL_LIST_COUNT; i++) + material_list_array[i] = {}; + } + + void DispManager::set_morph(object_info object, float_t weight) { + morph.weight = weight; + morph.object = object; + } + + void DispManager::set_culling_finc(bool(*func)(obj_bounding_sphere*, mat4*)) { + culling.func = func; + } + + void DispManager::set_shadow_type(shadow_type_enum type) { + if (type == SHADOW_CHARA || type == SHADOW_STAGE) + shadow_type = type; + } + + void DispManager::set_texture_color_coefficients(vec4& value) { + texture_color_coefficients = value; + } + + void DispManager::set_texture_color_offset(vec4& value) { + texture_color_offset = value; + } + + void DispManager::set_texture_pattern(int32_t count, texture_pattern_struct* value) { + if (count > TEXTURE_PATTERN_COUNT) + return; + + texture_pattern_count = count; + + if (count) + for (int32_t i = 0; i < count; i++) + texture_pattern_array[i] = value[i]; + else + for (int32_t i = 0; i < TEXTURE_PATTERN_COUNT; i++) + texture_pattern_array[i] = {}; + } + + void DispManager::set_texture_specular_coefficients(vec4& value) { + texture_specular_coefficients = value; + } + + void DispManager::set_texture_specular_offset(vec4& value) { + texture_specular_offset = value; + } + + void DispManager::set_texture_transform(int32_t count, texture_transform_struct* value) { + if (count > TEXTURE_TRANSFORM_COUNT) + return; + + texture_transform_count = count; + + if (count) + for (int32_t i = 0; i < count; i++) + texture_transform_array[i] = value[i]; + else + for (int32_t i = count; i < TEXTURE_TRANSFORM_COUNT; i++) + texture_transform_array[i] = {}; + } + + void DispManager::set_wet_param(float_t value) { + wet_param = value; + } } -void object_data::set_wet_param(float_t value) { - wet_param = value; +namespace rndr { + RenderManager::RenderManager() : pass_sw(), reflect_blur_num(), reflect_blur_filter(), sync_gpu(), + cpu_time(), gpu_time(), time(), draw_pass_3d(), reflect_type(), tex_index(), + multisample_framebuffer(), multisample_renderbuffer(), multisample(), show_vector_flags(), + show_vector_length(), show_vector_z_offset(), field_2F8(), effect_texture(), npr_param(), + field_31C(), field_31D(), field_31E(), field_31F(), field_320(), npr(), samplers() { + for (bool& i : pass_sw) + i = true; + reflect = true; + refract = true; + shadow = true; + opaque_z_sort = true; + alpha_z_sort = true; + for (bool& i : draw_pass_3d) + i = true; + + shadow_ptr = new ::shadow; + if (shadow_ptr) + shadow_ptr->init_data(); + + for (int32_t i = 0; i < 9; i++) { + const struc_189* v2 = &stru_140A24420[i]; + if (v2->type != GL_TEXTURE_2D) + continue; + + render_texture& rt = render_textures[i]; + rt.init(v2->width, v2->height, v2->max_level, v2->color_format, v2->depth_format); + rt.bind(); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + gl_state_bind_framebuffer(0); + + glCreateSamplers(18, samplers); + for (int32_t i = 0; i < 18; i++) { + GLuint sampler = samplers[i]; + + static const vec4 border_color = 0.0f; + glSamplerParameterfv(sampler, GL_TEXTURE_BORDER_COLOR, (GLfloat*)&border_color); + glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, + i % 2 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); + glSamplerParameteri(sampler, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + GLuint wrap_s; + switch (i / 2 % 3) { + case 0: + wrap_s = GL_CLAMP_TO_EDGE; + break; + case 1: + wrap_s = GL_REPEAT; + break; + case 2: + wrap_s = GL_MIRRORED_REPEAT; + break; + } + glSamplerParameteri(sampler, GL_TEXTURE_WRAP_S, wrap_s); + + GLenum wrap_t; + switch (i / 6 % 3) { + case 0: + wrap_t = GL_CLAMP_TO_EDGE; + break; + case 1: + wrap_t = GL_REPEAT; + break; + case 2: + wrap_t = GL_MIRRORED_REPEAT; + break; + } + glSamplerParameteri(sampler, GL_TEXTURE_WRAP_T, wrap_t); + } + } + + RenderManager::~RenderManager() { + glDeleteSamplers(18, samplers); + + if (multisample_framebuffer) { + glDeleteFramebuffers(1, &multisample_framebuffer); + multisample_framebuffer = 0; + } + + if (multisample_renderbuffer) { + glDeleteRenderbuffers(1, &multisample_renderbuffer); + multisample_renderbuffer = 0; + } + + delete shadow_ptr; + } + + void RenderManager::add_user(int32_t type, void(*func)(void*), void* data) { + user.push_back({ type, func, data }); + } + + void RenderManager::clear_user(int32_t type) { + for (std::list::iterator i = user.begin(); i != user.end(); i++) + if (i->type == type) { + user.erase(i); + break; + } + } + + render_texture& RenderManager::get_render_texture(int32_t index) { + return render_textures[stru_140A244E0[index][tex_index[index]]]; + } + + void RenderManager::resize(int32_t width, int32_t height) { + if (!multisample_framebuffer) + glGenFramebuffers(1, &multisample_framebuffer); + + if (!multisample_renderbuffer) + glGenRenderbuffers(1, &multisample_renderbuffer); + + glBindFramebuffer(GL_FRAMEBUFFER, multisample_framebuffer); + glBindRenderbuffer(GL_RENDERBUFFER, multisample_renderbuffer); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, 8, GL_RGBA8, width, height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, multisample_renderbuffer); + glDrawBuffer(GL_COLOR_ATTACHMENT0); + glReadBuffer(GL_COLOR_ATTACHMENT0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + } + + void RenderManager::set_effect_texture(texture* value) { + effect_texture = value; + } + + void RenderManager::set_multisample(bool value) { + multisample = value; + } + + void RenderManager::set_npr_param(int32_t value) { + npr_param = value; + } + + void RenderManager::set_pass_sw(RenderPassID id, bool value) { + pass_sw[id] = value; + } + + void RenderManager::set_reflect(bool value) { + reflect = value; + } + + void RenderManager::set_reflect_blur(int32_t reflect_blur_num, blur_filter_mode reflect_blur_filter) { + this->reflect_blur_num = reflect_blur_num; + this->reflect_blur_filter = reflect_blur_filter; + } + + void RenderManager::set_reflect_resolution_mode(reflect_refract_resolution_mode mode) { + tex_index[0] = mode; + } + + void RenderManager::set_reflect_type(stage_data_reflect_type type) { + reflect_type = type; + } + + void RenderManager::set_refract(bool value) { + refract = value; + } + + void RenderManager::set_refract_resolution_mode(reflect_refract_resolution_mode mode) { + tex_index[1] = mode; + } + + void RenderManager::set_shadow_false() { + shadow = false; + } + + void RenderManager::set_shadow_true() { + shadow = true; + } } extern float_t rob_frame; extern render_context* rctx_ptr; -render_context::render_context() : litproj(), -chara_reflect(), chara_refract(), view_mat(), matrix_buffer() { +void obj_scene_shader_data::set_g_irradiance_r_transforms(const mat4& mat) { + mat4 temp; + mat4_transpose(&mat, &temp); + g_irradiance_r_transforms[0] = temp.row0; + g_irradiance_r_transforms[1] = temp.row1; + g_irradiance_r_transforms[2] = temp.row2; + g_irradiance_r_transforms[3] = temp.row3; +} + +void obj_scene_shader_data::set_g_irradiance_g_transforms(const mat4& mat) { + mat4 temp; + mat4_transpose(&mat, &temp); + g_irradiance_g_transforms[0] = temp.row0; + g_irradiance_g_transforms[1] = temp.row1; + g_irradiance_g_transforms[2] = temp.row2; + g_irradiance_g_transforms[3] = temp.row3; +} + +void obj_scene_shader_data::set_g_irradiance_b_transforms(const mat4& mat) { + mat4 temp; + mat4_transpose(&mat, &temp); + g_irradiance_b_transforms[0] = temp.row0; + g_irradiance_b_transforms[1] = temp.row1; + g_irradiance_b_transforms[2] = temp.row2; + g_irradiance_b_transforms[3] = temp.row3; +} + +void obj_scene_shader_data::set_g_normal_tangent_transforms(const mat4& mat) { + mat4 temp; + mat4_transpose(&mat, &temp); + g_normal_tangent_transforms[0] = temp.row0; + g_normal_tangent_transforms[1] = temp.row1; + g_normal_tangent_transforms[2] = temp.row2; +} + +void obj_scene_shader_data::set_g_self_shadow_receivers(int32_t index, const mat4& mat) { + size_t _index = index * 3LL; + + mat4 temp; + mat4_transpose(&mat, &temp); + g_self_shadow_receivers[_index + 0] = temp.row0; + g_self_shadow_receivers[_index + 1] = temp.row1; + g_self_shadow_receivers[_index + 2] = temp.row2; +} + +void obj_scene_shader_data::set_g_light_projection(const mat4& mat) { + mat4 temp; + mat4_transpose(&mat, &temp); + g_light_projection[0] = temp.row0; + g_light_projection[1] = temp.row1; + g_light_projection[2] = temp.row2; + g_light_projection[3] = temp.row3; +} + +void obj_scene_shader_data::set_projection_view(const mat4& view, const mat4& proj) { + mat4 temp; + mat4_transpose(&view, &temp); + g_view[0] = temp.row0; + g_view[1] = temp.row1; + g_view[2] = temp.row2; + + mat4_inverse(&view, &temp); + mat4_transpose(&temp, &temp); + g_view_inverse[0] = temp.row0; + g_view_inverse[1] = temp.row1; + g_view_inverse[2] = temp.row2; + + mat4_mult(&view, &proj, &temp); + mat4_transpose(&temp, &temp); + g_projection_view[0] = temp.row0; + g_projection_view[1] = temp.row1; + g_projection_view[2] = temp.row2; + g_projection_view[3] = temp.row3; +} + +void obj_batch_shader_data::set_g_joint(const mat4& mat) { + mat4 temp; + mat4_transpose(&mat, &temp); + g_joint[0] = temp.row0; + g_joint[1] = temp.row1; + g_joint[2] = temp.row2; + + mat4_inverse(&mat, &temp); + mat4_transpose(&temp, &temp); + g_joint_inverse[0] = temp.row0; + g_joint_inverse[1] = temp.row1; + g_joint_inverse[2] = temp.row2; +} + +void obj_batch_shader_data::set_g_texcoord_transforms(int32_t index, const mat4& mat) { + size_t _index = index * 2LL; + + mat4 temp; + mat4_transpose(&mat, &temp); + g_texcoord_transforms[_index + 0] = temp.row0; + g_texcoord_transforms[_index + 1] = temp.row1; +} + +void obj_batch_shader_data::set_transforms(const mat4& model, const mat4& view, const mat4& proj) { + mat4 temp; + mat4_transpose(&model, &temp); + g_worlds[0] = temp.row0; + g_worlds[1] = temp.row1; + g_worlds[2] = temp.row2; + + mat4_inverse(&model, &temp); + g_worlds_invtrans[0] = temp.row0; + g_worlds_invtrans[1] = temp.row1; + g_worlds_invtrans[2] = temp.row2; + + mat4 mv; + mat4_mult(&model, &view, &mv); + + mat4_transpose(&mv, &temp); + g_worldview[0] = temp.row0; + g_worldview[1] = temp.row1; + g_worldview[2] = temp.row2; + + mat4_inverse(&mv, &temp); + mat4_transpose(&temp, &temp); + g_worldview_inverse[0] = temp.row0; + g_worldview_inverse[1] = temp.row1; + g_worldview_inverse[2] = temp.row2; + + mat4_mult(&mv, &proj, &temp); + mat4_transpose(&temp, &temp); + g_transforms[0] = temp.row0; + g_transforms[1] = temp.row1; + g_transforms[2] = temp.row2; + g_transforms[3] = temp.row3; +} + +render_context::render_context() : litproj(), chara_reflect(), +chara_refract(), view_mat(), matrix_buffer(), box_vao(), box_vbo() { camera = new ::camera; + + static const float_t box_texcoords[] = { + 1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 0.0f, 0.0f, -1.0f, + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, + -0.5f, 0.5f, 0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, + -0.5f, 0.5f, 0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, + -0.5f, 0.5f, 0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, + -0.5f, 0.5f, 0.5f, 0.5f, -0.5f, -0.5f, 0.5f, -0.5f, + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.5f, -1.5f, -1.5f, 0.5f, 1.5f, -0.5f, -0.5f, 1.5f, + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.5f, -1.5f, -1.5f, 0.5f, 1.5f, -0.5f, -0.5f, 1.5f, + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.5f, -1.5f, -1.5f, 0.5f, 1.5f, -0.5f, -0.5f, 1.5f, + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.5f, -1.5f, -1.5f, 0.5f, 1.5f, -0.5f, -0.5f, 1.5f, + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 3.5f, -3.5f, -1.5f, -3.5f, 1.5f, -1.5f, -3.5f, -1.5f, + 3.5f, 1.5f, -1.5f, 1.5f, 1.5f, 3.5f, -3.5f, 3.5f, + 3.5f, -3.5f, -1.5f, -3.5f, 1.5f, -1.5f, -3.5f, -1.5f, + 3.5f, 1.5f, -1.5f, 1.5f, 1.5f, 3.5f, -3.5f, 3.5f, + 3.5f, -3.5f, -1.5f, -3.5f, 1.5f, -1.5f, -3.5f, -1.5f, + 3.5f, 1.5f, -1.5f, 1.5f, 1.5f, 3.5f, -3.5f, 3.5f, + 3.5f, -3.5f, -1.5f, -3.5f, 1.5f, -1.5f, -3.5f, -1.5f, + 3.5f, 1.5f, -1.5f, 1.5f, 1.5f, 3.5f, -3.5f, 3.5f, + }; + + glGenVertexArrays(1, &box_vao); + gl_state_bind_vertex_array(box_vao); + + glGenBuffers(1, &box_vbo); + gl_state_bind_array_buffer(box_vbo, true); + if (GLAD_GL_VERSION_4_4) + glBufferStorage(GL_ARRAY_BUFFER, sizeof(box_texcoords), box_texcoords, 0); + else + glBufferData(GL_ARRAY_BUFFER, sizeof(box_texcoords), box_texcoords, GL_STATIC_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 64, (void*)0); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 64, (void*)16); + glEnableVertexAttribArray(2); + glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, 64, (void*)32); + glEnableVertexAttribArray(3); + glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, 64, (void*)48); + gl_state_bind_array_buffer(0); + gl_state_bind_vertex_array(0); + + contour_coef_ubo.Create(sizeof(contour_coef_shader_data)); + contour_params_ubo.Create(sizeof(contour_params_shader_data)); + filter_scene_ubo.Create(sizeof(filter_scene_shader_data)); + esm_filter_batch_ubo.Create(sizeof(esm_filter_batch_shader_data)); + imgfilter_batch_ubo.Create(sizeof(imgfilter_batch_shader_data)); + glass_eye_batch_ubo.Create(sizeof(glass_eye_batch_shader_data)); + quad_ubo.Create(sizeof(quad_shader_data)); + sss_filter_gaussian_coef_ubo.Create(sizeof(sss_filter_gaussian_coef_shader_data)); + transparency_batch_ubo.Create(sizeof(transparency_batch_shader_data)); + + obj_scene = {}; + obj_batch = {}; + obj_skinning = {}; + obj_scene_ubo.Create(sizeof(obj_scene_shader_data)); + obj_batch_ubo.Create(sizeof(obj_batch_shader_data)); + obj_skinning_ubo.Create(sizeof(obj_skinning_shader_data)); } render_context::~render_context() { + obj_skinning_ubo.Destroy(); + obj_batch_ubo.Destroy(); + obj_scene_ubo.Destroy(); + + transparency_batch_ubo.Destroy(); + sss_filter_gaussian_coef_ubo.Destroy(); + quad_ubo.Destroy(); + glass_eye_batch_ubo.Destroy(); + imgfilter_batch_ubo.Destroy(); + esm_filter_batch_ubo.Destroy(); + filter_scene_ubo.Destroy(); + contour_params_ubo.Destroy(); + contour_coef_ubo.Destroy(); + + glDeleteVertexArrays(1, &box_vao); + glDeleteBuffers(1, &box_vbo); + if (camera) { delete camera; camera = 0; @@ -1257,13 +2462,13 @@ void render_context::ctrl() { void render_context::disp() { rctx_ptr = this; - object_data.reset(); + disp_manager.refresh(); draw_state.stats_prev = draw_state.stats; draw_state.stats.reset(); app::TaskWork::Disp(); - draw_pass.shadow_ptr->ctrl(this); + render_manager.shadow_ptr->ctrl(this); post_process.ctrl(camera); - draw_pass_main(this); + render_manager.render_all(this); app::TaskWork::Basic(); } @@ -1423,8 +2628,8 @@ void render_context::light_param_data_ibl_set( if (fabsf(len - 1.0f) < 0.02f) l->set_position(ibl->lit_dir[0]); - l->set_ibl_specular(ibl->lit_col[0]); - l->set_ibl_back(ibl->lit_col[2]); + l->set_ibl_color0(ibl->lit_col[0]); + l->set_ibl_color1(ibl->lit_col[2]); l->set_ibl_direction(ibl->lit_dir[0]); l = &set->lights[LIGHT_STAGE]; @@ -1433,7 +2638,7 @@ void render_context::light_param_data_ibl_set( if (fabsf(len - 1.0f) < 0.02f) l->set_position(ibl->lit_dir[1]); - l->set_ibl_specular(ibl->lit_col[1]); + l->set_ibl_color0(ibl->lit_col[1]); l->set_ibl_direction(ibl->lit_dir[1]); } @@ -1511,7 +2716,7 @@ void shadow::ctrl(render_context* rctx) { for (int32_t i = 0; i < 2; i++) field_2F0[i] = false; - if (rctx->draw_pass.shadow) { + if (rctx->render_manager.shadow) { view_mat[0] = rctx->camera->view; view_mat[1] = rctx->camera->inv_view; @@ -1527,7 +2732,7 @@ void shadow::ctrl(render_context* rctx) { direction = -position * (1.0f / length); for (int32_t i = 0; i < 2; i++) - if (draw_task_get_count(rctx, (draw_object_type)((int32_t)DRAW_OBJECT_SHADOW_CHARA + i))) + if (rctx->disp_manager.get_obj_count((mdl::ObjType)((int32_t)mdl::OBJ_TYPE_SHADOW_CHARA + i))) field_2F0[i] = true; } @@ -1769,16 +2974,17 @@ int32_t shadow::init_data() { { 0x800, 0x800, 0, GL_R32F, GL_ZERO }, { 0x200, 0x200, 0, GL_R32F, GL_ZERO }, { 0x200, 0x200, 0, GL_R32F, GL_ZERO }, + { 0x200, 0x200, 3, GL_RGBA8, GL_ZERO }, // Extra for buf }; shadow_texture_init_params* v3 = init_params; - for (int32_t i = 0; i < 7; i++, v3++) + for (int32_t i = 0; i < 8; i++, v3++) if (field_8[i].init(v3->width, v3->height, v3->max_level, v3->color_format, v3->depth_format) < 0) return -1; - for (int32_t i = 0; i < 3; i++) { - gl_state_bind_texture_2d(field_8[i].color_texture->tex); + for (int32_t i = 0; i < 4; i++) { + gl_state_bind_texture_2d(field_8[i == 3 ? 7 : i].color_texture->tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); static const vec4 border_color = 1.0f; @@ -1789,13 +2995,13 @@ int32_t shadow::init_data() { return 0; } -static void object_data_get_vertex_attrib_buffer_bindings(draw_object* draw, +static void object_data_get_vertex_attrib_buffer_bindings(const mdl::ObjSubMeshArgs* args, int32_t texcoord_array[2], GLuint vertex_attrib_buffer_binding[16]) { - obj_mesh* mesh = draw->mesh; - obj_sub_mesh* sub_mesh = draw->sub_mesh; + const obj_mesh* mesh = args->mesh; + const obj_sub_mesh* sub_mesh = args->sub_mesh; - GLuint array_buffer = draw->array_buffer; - GLuint morph_array_buffer = draw->morph_array_buffer; + GLuint array_buffer = args->array_buffer; + GLuint morph_array_buffer = args->morph_array_buffer; bool compressed = mesh->attrib.m.compressed; GLsizei size_vertex = (GLsizei)mesh->size_vertex; @@ -1826,7 +3032,7 @@ static void object_data_get_vertex_attrib_buffer_bindings(draw_object* draw, if (!compressed && vertex_format & OBJ_VERTEX_UNKNOWN) vertex_attrib_buffer_binding[UNKNOWN_INDEX] = array_buffer; - if (draw->morph_array_buffer) { + if (args->morph_array_buffer) { if (vertex_format & OBJ_VERTEX_POSITION) vertex_attrib_buffer_binding[MORPH_POSITION_INDEX] = morph_array_buffer; if (vertex_format & OBJ_VERTEX_NORMAL) diff --git a/src/CRE/render_context.hpp b/src/CRE/render_context.hpp index 9f0ef4ed..de2fee41 100644 --- a/src/CRE/render_context.hpp +++ b/src/CRE/render_context.hpp @@ -6,7 +6,9 @@ #pragma once #include "../KKdLib/default.hpp" +#include "../KKdLib/time.hpp" #include "../KKdLib/vec.hpp" +#include "GL/uniform_buffer.hpp" #include "light_param/face.hpp" #include "light_param/fog.hpp" #include "light_param/light.hpp" @@ -19,7 +21,6 @@ #include "post_process.hpp" #include "render_texture.hpp" #include "task.hpp" -#include "time.hpp" #include "static_var.hpp" #define MATRIX_BUFFER_COUNT 320 @@ -28,67 +29,11 @@ #define TEXTURE_TRANSFORM_COUNT 24 enum blur_filter_mode { - BLUR_FILTER_4 = 0x00, - BLUR_FILTER_9 = 0x01, - BLUR_FILTER_16 = 0x02, - BLUR_FILTER_32 = 0x03, -}; - -enum draw_object_type { - DRAW_OBJECT_OPAQUE = 0x00, - DRAW_OBJECT_TRANSLUCENT = 0x01, - DRAW_OBJECT_TRANSLUCENT_NO_SHADOW = 0x02, - DRAW_OBJECT_TRANSPARENT = 0x03, - DRAW_OBJECT_SHADOW_CHARA = 0x04, - DRAW_OBJECT_SHADOW_STAGE = 0x05, - DRAW_OBJECT_TYPE_6 = 0x06, - DRAW_OBJECT_TYPE_7 = 0x07, - DRAW_OBJECT_SHADOW_OBJECT_CHARA = 0x08, - DRAW_OBJECT_SHADOW_OBJECT_STAGE = 0x09, - DRAW_OBJECT_REFLECT_CHARA_OPAQUE = 0x0A, - DRAW_OBJECT_REFLECT_CHARA_TRANSLUCENT = 0x0B, - DRAW_OBJECT_REFLECT_CHARA_TRANSPARENT = 0x0C, - DRAW_OBJECT_REFLECT_OPAQUE = 0x0D, - DRAW_OBJECT_REFLECT_TRANSLUCENT = 0x0E, - DRAW_OBJECT_REFLECT_TRANSPARENT = 0x0F, - DRAW_OBJECT_REFRACT_OPAQUE = 0x10, - DRAW_OBJECT_REFRACT_TRANSLUCENT = 0x11, - DRAW_OBJECT_REFRACT_TRANSPARENT = 0x12, - DRAW_OBJECT_SSS = 0x13, - DRAW_OBJECT_OPAQUE_ALPHA_ORDER_1 = 0x14, - DRAW_OBJECT_TRANSPARENT_ALPHA_ORDER_1 = 0x15, - DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_1 = 0x16, - DRAW_OBJECT_OPAQUE_ALPHA_ORDER_2 = 0x17, - DRAW_OBJECT_TRANSPARENT_ALPHA_ORDER_2 = 0x18, - DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_2 = 0x19, - DRAW_OBJECT_OPAQUE_ALPHA_ORDER_3 = 0x1A, - DRAW_OBJECT_TRANSPARENT_ALPHA_ORDER_3 = 0x1B, - DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_3 = 0x1C, - DRAW_OBJECT_PREPROCESS = 0x1D, - DRAW_OBJECT_OPAQUE_LOCAL = 0x1E, // X - DRAW_OBJECT_TRANSLUCENT_LOCAL = 0x1F, - DRAW_OBJECT_TRANSPARENT_LOCAL = 0x20, - DRAW_OBJECT_OPAQUE_ALPHA_ORDER_2_LOCAL = 0x21, - DRAW_OBJECT_TRANSPARENT_ALPHA_ORDER_2_LOCAL = 0x22, - DRAW_OBJECT_TRANSLUCENT_ALPHA_ORDER_2_LOCAL = 0x23, - DRAW_OBJECT_MAX = 0x24, -}; - -enum draw_pass_type { - DRAW_PASS_SHADOW = 0x00, - DRAW_PASS_SS_SSS = 0x01, - DRAW_PASS_TYPE_2 = 0x02, - DRAW_PASS_REFLECT = 0x03, - DRAW_PASS_REFRACT = 0x04, - DRAW_PASS_PRE_PROCESS = 0x05, - DRAW_PASS_CLEAR = 0x06, - DRAW_PASS_PRE_SPRITE = 0x07, - DRAW_PASS_3D = 0x08, - DRAW_PASS_SHOW_VECTOR = 0x09, - DRAW_PASS_POST_PROCESS = 0x0A, - DRAW_PASS_SPRITE = 0x0B, - DRAW_PASS_TYPE_12 = 0x0C, - DRAW_PASS_MAX = 0x0D, + BLUR_FILTER_4 = 0, + BLUR_FILTER_9, + BLUR_FILTER_16, + BLUR_FILTER_32, + BLUR_FILTER_MAX, }; enum draw_task_flags : uint32_t { @@ -116,7 +61,7 @@ enum draw_task_flags : uint32_t { DRAW_TASK_200000 = 0x00200000, DRAW_TASK_400000 = 0x00400000, DRAW_TASK_800000 = 0x00800000, - DRAW_TASK_PREPROCESS = 0x01000000, + DRAW_TASK_USER = 0x01000000, DRAW_TASK_2000000 = 0x02000000, DRAW_TASK_4000000 = 0x04000000, DRAW_TASK_8000000 = 0x08000000, @@ -126,25 +71,106 @@ enum draw_task_flags : uint32_t { DRAW_TASK_NO_TRANSLUCENCY = 0x80000000, }; -enum draw_task_type { - DRAW_TASK_OBJECT = 0x00, - DRAW_TASK_OBJECT_PRIMITIVE = 0x01, - DRAW_TASK_OBJECT_PREPROCESS = 0x02, - DRAW_TASK_OBJECT_TRANSLUCENT = 0x03, -}; - enum reflect_refract_resolution_mode { - REFLECT_REFRACT_RESOLUTION_256x256 = 0x00, - REFLECT_REFRACT_RESOLUTION_512x256 = 0x01, - REFLECT_REFRACT_RESOLUTION_512x512 = 0x02, + REFLECT_REFRACT_RESOLUTION_256x256 = 0, + REFLECT_REFRACT_RESOLUTION_512x256, + REFLECT_REFRACT_RESOLUTION_512x512, + REFLECT_REFRACT_RESOLUTION_MAX, }; enum shadow_type_enum { - SHADOW_CHARA = 0x00, - SHADOW_STAGE = 0x01, - SHADOW_MAX = 0x02, + SHADOW_CHARA = 0, + SHADOW_STAGE, + SHADOW_MAX, }; +namespace mdl { + enum EtcObjType { + ETC_OBJ_TEAPOT = 0, + ETC_OBJ_GRID, + ETC_OBJ_CUBE, + ETC_OBJ_SPHERE, + ETC_OBJ_PLANE, + ETC_OBJ_CONE, + ETC_OBJ_LINE, + ETC_OBJ_CROSS, + ETC_OBJ_MAX, + }; + + enum ObjKind { + OBJ_KIND_NORMAL = 0, + OBJ_KIND_ETC, + OBJ_KIND_USER, + OBJ_KIND_TRANSLUCENT, + OBJ_KIND_MAX, + }; + + enum ObjType { + OBJ_TYPE_OPAQUE = 0, + OBJ_TYPE_OPAQUE_LITPROJ_0, // Added + OBJ_TYPE_OPAQUE_LITPROJ_1, // Added + OBJ_TYPE_TRANSLUCENT, + OBJ_TYPE_TRANSLUCENT_LITPROJ_0, // Added + OBJ_TYPE_TRANSLUCENT_LITPROJ_1, // Added + OBJ_TYPE_TRANSLUCENT_NO_SHADOW, + OBJ_TYPE_TRANSPARENT, + OBJ_TYPE_TRANSPARENT_LITPROJ_0, // Added + OBJ_TYPE_TRANSPARENT_LITPROJ_1, // Added + OBJ_TYPE_SHADOW_CHARA, + OBJ_TYPE_SHADOW_STAGE, + OBJ_TYPE_TYPE_6, + OBJ_TYPE_TYPE_7, + OBJ_TYPE_SHADOW_OBJECT_CHARA, + OBJ_TYPE_SHADOW_OBJECT_STAGE, + OBJ_TYPE_REFLECT_CHARA_OPAQUE, + OBJ_TYPE_REFLECT_CHARA_TRANSLUCENT, + OBJ_TYPE_REFLECT_CHARA_TRANSPARENT, + OBJ_TYPE_REFLECT_OPAQUE, + OBJ_TYPE_REFLECT_TRANSLUCENT, + OBJ_TYPE_REFLECT_TRANSPARENT, + OBJ_TYPE_REFRACT_OPAQUE, + OBJ_TYPE_REFRACT_TRANSLUCENT, + OBJ_TYPE_REFRACT_TRANSPARENT, + OBJ_TYPE_SSS, + OBJ_TYPE_OPAQUE_ALPHA_ORDER_1, + OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_1, + OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_1, + OBJ_TYPE_OPAQUE_ALPHA_ORDER_2, + OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_2, + OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_2, + OBJ_TYPE_OPAQUE_ALPHA_ORDER_3, + OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_3, + OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_3, + OBJ_TYPE_USER, + OBJ_TYPE_OPAQUE_LOCAL, // X + OBJ_TYPE_TRANSLUCENT_LOCAL, + OBJ_TYPE_TRANSPARENT_LOCAL, + OBJ_TYPE_OPAQUE_ALPHA_ORDER_2_LOCAL, + OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_2_LOCAL, + OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_2_LOCAL, + OBJ_TYPE_MAX, + }; +} + +namespace rndr { + enum RenderPassID { + RND_PASSID_SHADOWMAP = 0, + RND_PASSID_SS_SSS, + RND_PASSID_2, + RND_PASSID_REFLECT, + RND_PASSID_REFRACT, + RND_PASSID_USER, + RND_PASSID_CLEAR, + RND_PASSID_SPRITE_BG, + RND_PASSID_ALL_3D, + RND_PASSID_SHOW_VECTOR, + RND_PASSID_POSTPROCESS, + RND_PASSID_SPRITE_FG, + RND_PASSID_12, + RND_PASSID_NUM, + }; +} + struct render_context; struct shadow; @@ -174,70 +200,6 @@ struct sss_data { ~sss_data(); }; -struct draw_preprocess { - int32_t type; - void(*func)(void*); - void* data; -}; - -struct draw_pass { - bool enable[DRAW_PASS_MAX]; - shadow* shadow_ptr; - bool reflect; - bool refract; - int32_t reflect_blur_num; - blur_filter_mode reflect_blur_filter; - bool wait_for_gpu; - double_t cpu_time[DRAW_PASS_MAX]; - double_t gpu_time[DRAW_PASS_MAX]; - time_struct time; - bool shadow; - bool opaque_z_sort; - bool alpha_z_sort; - bool draw_pass_3d[DRAW_PASS_3D_MAX]; - stage_data_reflect_type reflect_type; - int32_t tex_index[11]; - render_texture render_textures[8]; - GLuint multisample_framebuffer; - GLuint multisample_renderbuffer; - bool multisample; - int32_t show_vector_flags; - float_t show_vector_length; - float_t show_vector_z_offset; - bool field_2F8; - std::list preprocess; - texture* effect_texture; - int32_t npr_param; - bool field_31C; - bool field_31D; - bool field_31E; - bool field_31F; - bool field_320; - bool npr; - sss_data sss_data; - GLuint samplers[18]; - - draw_pass(); - ~draw_pass(); - - void add_preprocess(int32_t type, void(*func)(void*), void* data); - void clear_preprocess(int32_t type); - render_texture& get_render_texture(int32_t index); - void resize(int32_t width, int32_t height); - void set_enable(draw_pass_type type, bool value); - void set_effect_texture(texture* value); - void set_multisample(bool value); - void set_npr_param(int32_t value); - void set_reflect(bool value); - void set_reflect_blur(int32_t reflect_blur_num, blur_filter_mode reflect_blur_filter); - void set_reflect_resolution_mode(reflect_refract_resolution_mode mode); - void set_reflect_type(stage_data_reflect_type type); - void set_refract(bool value); - void set_refract_resolution_mode(reflect_refract_resolution_mode mode); - void set_shadow_false(); - void set_shadow_true(); -}; - struct draw_state { draw_state_stats stats; draw_state_stats stats_prev; @@ -265,6 +227,12 @@ struct draw_state { void set_fog_height(bool value); }; +struct draw_user { + int32_t type; + void(*func)(void*); + void* data; +}; + struct material_list_struct { uint32_t hash; vec4 blend_color; @@ -293,138 +261,145 @@ struct texture_transform_struct { texture_transform_struct(uint32_t id, mat4& mat); }; -struct draw_object; +namespace mdl { + struct DispManager; + struct ObjSubMeshArgs; -struct draw_object { - obj_sub_mesh* sub_mesh; - obj_mesh* mesh; - obj_material_data* material; - std::vector* textures; - int32_t mat_count; - mat4* mats; - GLuint vertex_array; - GLuint array_buffer; - GLuint element_array_buffer; - bool set_blend_color; - bool chara_color; - vec4 blend_color; - vec4 emission; - bool self_shadow; - shadow_type_enum shadow; - GLuint morph_array_buffer; - float_t morph_value; - int32_t texture_pattern_count; - texture_pattern_struct texture_pattern_array[TEXTURE_PATTERN_COUNT]; - vec4 texture_color_coeff; - vec4 texture_color_offset; - vec4 texture_specular_coeff; - vec4 texture_specular_offset; - int32_t texture_transform_count; - texture_transform_struct texture_transform_array[TEXTURE_TRANSFORM_COUNT]; - int32_t instances_count; - mat4* instances_mat; - void(*draw_object_func)(draw_object*); - bool vertex_attrib_array[16]; -}; + struct ObjSubMeshArgs { + const obj_sub_mesh* sub_mesh; + const obj_mesh* mesh; + const obj_material_data* material; + const std::vector* textures; + int32_t mat_count; + const mat4* mats; + GLuint vertex_array; + GLuint array_buffer; + GLuint element_array_buffer; + bool set_blend_color; + bool chara_color; + vec4 blend_color; + vec4 emission; + bool self_shadow; + shadow_type_enum shadow; + GLuint morph_array_buffer; + float_t morph_weight; + int32_t texture_pattern_count; + texture_pattern_struct texture_pattern_array[TEXTURE_PATTERN_COUNT]; + vec4 texture_color_coefficients; + vec4 texture_color_offset; + vec4 texture_specular_coefficients; + vec4 texture_specular_offset; + int32_t texture_transform_count; + texture_transform_struct texture_transform_array[TEXTURE_TRANSFORM_COUNT]; + int32_t instances_count; + const mat4* instances_mat; + void(*func)(const mdl::ObjSubMeshArgs*); + }; -enum draw_primitive_type { - DRAW_PRIMITIVE_TEAPOT = 0x00, - DRAW_PRIMITIVE_TYPE_1 = 0x01, - DRAW_PRIMITIVE_CUBE = 0x02, - DRAW_PRIMITIVE_SPHERE = 0x03, - DRAW_PRIMITIVE_TYPE_4 = 0x04, - DRAW_PRIMITIVE_CONE = 0x05, - DRAW_PRIMITIVE_LINE = 0x06, - DRAW_PRIMITIVE_TYPE_7 = 0x07, -}; + struct EtcObjTeapot { + float_t size; + }; -struct draw_primitive_teapot { - float_t size; -}; + struct EtcObjGrid { + int32_t w; + int32_t h; + int32_t ws; + int32_t hs; + }; -struct draw_primitive_type_1 { - int32_t field_0; - int32_t field_4; - int32_t field_8; - int32_t field_C; -}; + struct EtcObjCube { + float_t size_x; + float_t size_y; + float_t size_z; + bool wire; + }; -struct draw_primitive_cube { - vec3 size; - bool wireframe; -}; + struct EtcObjSphere { + float_t radius; + int32_t slices; + int32_t stacks; + bool wire; + }; -struct draw_primitive_sphere { - float_t radius; - int32_t slices; - int32_t stacks; - bool wireframe; -}; + struct EtcObjPlane { + int32_t w; + int32_t h; + }; -struct draw_primitive_type_4 { - int32_t field_0; - int32_t field_4; -}; + struct EtcObjCone { + float_t base; + float_t height; + int32_t slices; + int32_t stacks; + bool wire; + }; -struct draw_primitive_cone { - float_t base; - float_t height; - int32_t slices; - int32_t stacks; - bool wireframe; -}; + struct EtcObjLine { + float_t x0; + float_t y0; + float_t z0; + float_t x1; + float_t y1; + float_t z1; + }; -struct draw_primitive_line { - vec3 v0; - vec3 v1; -}; + struct EtcObjCross { + float_t size; + }; -struct draw_primitive_type_7 { - float_t size; -}; + struct EtcObj { + union Data { + EtcObjTeapot teapot; + EtcObjGrid grid; + EtcObjCube cube; + EtcObjSphere sphere; + EtcObjPlane plane; + EtcObjCone cone; + EtcObjLine line; + EtcObjCross cross; + }; -union draw_primitive_union { - draw_primitive_teapot teapot; - draw_primitive_type_1 field_1; - draw_primitive_cube cube; - draw_primitive_sphere sphere; - draw_primitive_type_4 type_4; - draw_primitive_cone cone; - draw_primitive_line line; - draw_primitive_type_7 type_7; -}; + EtcObjType type; + vec4 color; + bool fog; + Data data; + }; + + typedef void(*UserArgsFunc)(render_context* rctx, void* data); -struct draw_primitive { - draw_primitive_type type; - bool fog; - vec4 color; - draw_primitive_union data; -}; + struct UserArgs { + UserArgsFunc func; + void* data; + }; -struct draw_task_preprocess { - void(*func)(render_context* rctx, void* data); - void* data; -}; + struct ObjTranslucentArgs { + uint32_t count; + ObjSubMeshArgs* sub_mesh[40]; + }; + + struct ObjData { + union Args { + ObjSubMeshArgs sub_mesh; + EtcObj::Data etc; + UserArgs user; + ObjTranslucentArgs translucent; + }; -struct draw_task_object_translucent { - uint32_t count; - draw_object* objects[40]; -}; + ObjKind kind; + mat4 mat; + float_t view_z; + float_t radius; + Args args; -union draw_task_union { - draw_object object; - draw_primitive primitive; - draw_task_preprocess preprocess; - draw_task_object_translucent object_translucent; -}; - -struct draw_task { - draw_task_type type; - mat4 mat; - float_t depth; - float_t bounding_radius; - draw_task_union data; -}; + void init_sub_mesh(DispManager* disp_manager, const mat4* mat, + float_t radius, obj_sub_mesh* sub_mesh, obj_mesh* mesh, obj_material_data* material, + std::vector* textures, int32_t mat_count, mat4* mats, GLuint array_buffer, + GLuint element_array_buffer, vec4* blend_color, vec4* emission, int32_t morph_array_buffer, + int32_t instances_count, mat4* instances_mat, void(*func)(const ObjSubMeshArgs*)); + void init_translucent(const mat4* mat, ObjTranslucentArgs* translucent); + void init_user(const mat4* mat, UserArgsFunc func, void* data); + }; +} struct light_proj { bool enable; @@ -438,122 +413,396 @@ struct light_proj { void resize(int32_t width, int32_t height); bool set(render_context* rctx); - static void get_proj_mat(vec3* view_point, vec3* interest, float_t fov, mat4* mat); static bool set_mat(render_context* rctx, bool set_mat); }; struct morph_struct { object_info object; - float_t value; + float_t weight; morph_struct(); }; -struct object_data_buffer { - int32_t offset; - int32_t max_offset; - int32_t size; //0x300000 - void* data; +struct texture_data_struct { + int32_t field_0; + vec3 texture_color_coefficients; + vec3 texture_color_offset; + vec3 texture_specular_coefficients; + vec3 texture_specular_offset; - object_data_buffer(); - ~object_data_buffer(); - - draw_task* add_draw_task(draw_task_type type); - mat4* add_mat4(int32_t count); - void reset(); + texture_data_struct(); }; -struct object_data_culling_info { - int32_t objects; - int32_t meshes; - int32_t submesh_array; +namespace mdl { + struct CullingCheck { + struct Info { + int32_t objects; + int32_t meshes; + int32_t submesh_array; + }; + + Info passed; + Info culled; + Info passed_prev; + Info culled_prev; + + bool(*func)(obj_bounding_sphere*, mat4*); + }; + + struct DispManager { + struct vertex_array { + GLuint array_buffer; + GLuint morph_array_buffer; + GLuint element_array_buffer; + int32_t alive_time; + GLuint vertex_array; + bool vertex_attrib_array[16]; + obj_vertex_format vertex_format; + GLsizei size_vertex; + bool compressed; + GLuint vertex_attrib_buffer_binding[16]; + int32_t texcoord_array[2]; + }; + + draw_task_flags draw_task_flags; + shadow_type_enum shadow_type; + int32_t field_8; + int32_t field_C; + std::vector obj[mdl::OBJ_TYPE_MAX]; + mdl::CullingCheck culling; + mat4 culling_mat; + int32_t field_230; + bool show_alpha_center; + bool show_mat_center; + bool object_culling; + bool object_sort; + bool chara_color; + int32_t buff_size; + int32_t buff_max; + int32_t buff_offset; + void* buff; + morph_struct morph; + int32_t texture_pattern_count; + texture_pattern_struct texture_pattern_array[TEXTURE_PATTERN_COUNT]; + vec4 texture_color_coefficients; + vec4 texture_color_offset; + vec4 texture_specular_coefficients; + vec4 texture_specular_offset; + float_t wet_param; + int32_t texture_transform_count; + texture_transform_struct texture_transform_array[TEXTURE_TRANSFORM_COUNT]; + int32_t material_list_count; + material_list_struct material_list_array[MATERIAL_LIST_COUNT]; + std::vector vertex_array_cache; + + DispManager(); + ~DispManager(); + + void add_vertex_array(const ObjSubMeshArgs* args); + ObjData* alloc_data(ObjKind kind); + mat4* alloc_data(int32_t count); + void buffer_reset(); + void check_vertex_arrays(); + void draw(mdl::ObjType type, int32_t depth_mask = 0, bool a4 = true); + void draw_translucent(mdl::ObjType type, int32_t alpha); + /*void draw_show_vector(mdl::ObjType type, int32_t show_vector);*/ + void entry_list(ObjType type, ObjData* data); + bool entry_obj(::obj* object, obj_mesh_vertex_buffer* obj_vertex_buf, + obj_mesh_index_buffer* obj_index_buf, const mat4* mat, + std::vector* textures, vec4* blend_color, mat4* bone_mat, ::obj* object_morph, + obj_mesh_vertex_buffer* obj_morph_vertex_buf, int32_t instances_count, + mat4* instances_mat, void(*func)(const mdl::ObjSubMeshArgs*), bool enable_bone_mat, bool local = false); + void entry_obj_by_obj(const mat4* mat, + ::obj* obj, std::vector* textures, obj_mesh_vertex_buffer* obj_vert_buf, + obj_mesh_index_buffer* obj_index_buf, mat4* bone_mat, float_t alpha); + bool entry_obj_by_object_info(const mat4* mat, object_info obj_info, mat4* bone_mat = 0); + bool entry_obj_by_object_info(const mat4* mat, object_info obj_info, + vec4* blend_color, mat4* bone_mat, int32_t instances_count, + mat4* instances_mat, void(*func)(const mdl::ObjSubMeshArgs*), bool enable_bone_mat, bool local = false); + bool entry_obj_by_object_info(const mat4* mat, object_info obj_info, float_t alpha, mat4* bone_mat = 0); + bool entry_obj_by_object_info(const mat4* mat, object_info obj_info, + float_t r, float_t g, float_t b, float_t a, mat4* bone_mat = 0, bool local = false); + bool entry_obj_by_object_info(const mat4* mat, object_info obj_info, + vec4* blend_color, mat4* bone_mat = 0, bool local = false); + void entry_obj_by_object_info_object_skin(object_info obj_info, + std::vector* texture_pattern, texture_data_struct* texture_data, float_t alpha, + mat4* matrices, mat4* ex_data_matrices, const mat4* mat, const mat4* global_mat); + void entry_obj_user(const mat4* mat, UserArgsFunc func, void* data, ObjType type); + GLuint get_vertex_array(const ObjSubMeshArgs* args); + bool get_chara_color(); + ::draw_task_flags get_draw_task_flags(); + void get_material_list(int32_t& count, material_list_struct*& value); + void get_morph(object_info& object, float_t& weight); + int32_t get_obj_count(ObjType type); + shadow_type_enum get_shadow_type(); + void get_texture_color_coeff(vec4& value); + void get_texture_color_offset(vec4& value); + void get_texture_pattern(int32_t& count, texture_pattern_struct*& value); + void get_texture_specular_coeff(vec4& value); + void get_texture_specular_offset(vec4& value); + void get_texture_transform(int32_t& count, texture_transform_struct*& value); + float_t get_wet_param(); + void obj_sort(mat4* view, ObjType type, int32_t compare_func); + void refresh(); + void set_chara_color(bool value = false); + void set_draw_task_flags(::draw_task_flags flags = (::draw_task_flags)0); + void set_material_list(int32_t count = 0, material_list_struct* value = 0); + void set_morph(object_info object = {}, float_t weight = 0.0f); + void set_culling_finc(bool(*func)(obj_bounding_sphere*, mat4*) = 0); + void set_shadow_type(shadow_type_enum type = SHADOW_CHARA); + void set_texture_color_coefficients(vec4& value); + void set_texture_color_offset(vec4& value); + void set_texture_pattern(int32_t count = 0, texture_pattern_struct* value = 0); + void set_texture_specular_coefficients(vec4& value); + void set_texture_specular_offset(vec4& value); + void set_texture_transform(int32_t count = 0, texture_transform_struct* value = 0); + void set_wet_param(float_t value = 0.0f); + }; +} + +namespace rndr { + struct RenderManager { + bool pass_sw[rndr::RND_PASSID_NUM]; + shadow* shadow_ptr; + bool reflect; + bool refract; + int32_t reflect_blur_num; + blur_filter_mode reflect_blur_filter; + bool sync_gpu; + double_t cpu_time[rndr::RND_PASSID_NUM]; + double_t gpu_time[rndr::RND_PASSID_NUM]; + time_struct time; + bool shadow; + bool opaque_z_sort; + bool alpha_z_sort; + bool draw_pass_3d[DRAW_PASS_3D_MAX]; + stage_data_reflect_type reflect_type; + int32_t tex_index[12]; // Extra for buf + render_texture render_textures[9]; // Extra for buf + GLuint multisample_framebuffer; + GLuint multisample_renderbuffer; + bool multisample; + int32_t show_vector_flags; + float_t show_vector_length; + float_t show_vector_z_offset; + bool field_2F8; + std::list user; + texture* effect_texture; + int32_t npr_param; + bool field_31C; + bool field_31D; + bool field_31E; + bool field_31F; + bool field_320; + bool npr; + sss_data sss_data; + GLuint samplers[18]; + + RenderManager(); + ~RenderManager(); + + void add_user(int32_t type, void(*func)(void*), void* data); + void clear_user(int32_t type); + render_texture& get_render_texture(int32_t index); + void resize(int32_t width, int32_t height); + void set_effect_texture(texture* value); + void set_multisample(bool value); + void set_npr_param(int32_t value); + void set_pass_sw(rndr::RenderPassID id, bool value); + void set_reflect(bool value); + void set_reflect_blur(int32_t reflect_blur_num, blur_filter_mode reflect_blur_filter); + void set_reflect_resolution_mode(reflect_refract_resolution_mode mode); + void set_reflect_type(stage_data_reflect_type type); + void set_refract(bool value); + void set_refract_resolution_mode(reflect_refract_resolution_mode mode); + void set_shadow_false(); + void set_shadow_true(); + + void render_all(render_context* rctx); + + void render_single_pass(rndr::RenderPassID id, render_context* rctx); + + void render_pass_begin(); + void render_pass_end(rndr::RenderPassID id); + + void pass_shadowmap(render_context* rctx); + void pass_ss_sss(render_context* rctx); + void pass_reflect(render_context* rctx); + void pass_refract(render_context* rctx); + void pass_user(render_context* rctx); + void pass_clear(render_context* rctx); + void pass_sprite_bg(render_context* rctx); + void pass_all_3d(render_context* rctx); + void pass_show_vector(render_context* rctx); + void pass_postprocess(render_context* rctx); + void pass_sprite_fg(render_context* rctx); + + void pass_3d_contour(render_context* rctx); + void pass_sprite_fg_surf(render_context* rctx); + }; +} + +struct contour_coef_shader_data { + vec4 g_contour; + vec4 g_near_far; }; -struct object_data_vertex_array { - GLuint array_buffer; - GLuint morph_array_buffer; - GLuint element_array_buffer; - int32_t alive_time; - GLuint vertex_array; - bool vertex_attrib_array[16]; - obj_vertex_format vertex_format; - GLsizei size_vertex; - bool compressed; - GLuint vertex_attrib_buffer_binding[16]; - int32_t texcoord_array[2]; +struct contour_params_shader_data { + vec4 g_near_far; }; -struct object_data { - draw_task_flags draw_task_flags; - shadow_type_enum shadow_type; - int32_t field_8; - int32_t field_C; - std::vector draw_task_array[DRAW_OBJECT_MAX]; - object_data_culling_info passed; - object_data_culling_info culled; - object_data_culling_info passed_prev; - object_data_culling_info culled_prev; - int32_t field_230; - bool show_alpha_center; - bool show_mat_center; - bool object_culling; - bool object_sort; - bool chara_color; - object_data_buffer buffer; - morph_struct morph; - int32_t texture_pattern_count; - texture_pattern_struct texture_pattern_array[TEXTURE_PATTERN_COUNT]; - vec4 texture_color_coeff; - vec4 texture_color_offset; - vec4 texture_specular_coeff; - vec4 texture_specular_offset; - float_t wet_param; - int32_t texture_transform_count; - texture_transform_struct texture_transform_array[TEXTURE_TRANSFORM_COUNT]; - int32_t material_list_count; - material_list_struct material_list_array[MATERIAL_LIST_COUNT]; - bool(*object_bounding_sphere_check_func)(obj_bounding_sphere*, camera*); - std::vector vertex_array_cache; +struct filter_scene_shader_data { + vec4 g_transform; + vec4 g_texcoord; +}; - object_data(); - ~object_data(); +struct esm_filter_batch_shader_data { + vec4 g_params; + vec4 g_gauss[2]; +}; - void add_vertex_array(draw_object* draw); - void check_vertex_arrays(); - GLuint get_vertex_array(draw_object* draw); - bool get_chara_color(); - ::draw_task_flags get_draw_task_flags(); - void get_material_list(int32_t& count, material_list_struct*& value); - void get_morph(object_info* object, float_t* value); - shadow_type_enum get_shadow_type(); - void get_texture_color_coeff(vec4& value); - void get_texture_color_offset(vec4& value); - void get_texture_pattern(int32_t& count, texture_pattern_struct*& value); - void get_texture_specular_coeff(vec4& value); - void get_texture_specular_offset(vec4& value); - void get_texture_transform(int32_t& count, texture_transform_struct*& value); - float_t get_wet_param(); - void reset(); - void set_chara_color(bool value = false); - void set_draw_task_flags(::draw_task_flags flags = (::draw_task_flags)0); - void set_material_list(int32_t count = 0, material_list_struct* value = 0); - void set_morph(object_info object = {}, float_t value = 0.0f); - void set_object_bounding_sphere_check_func(bool(*func)(obj_bounding_sphere*, camera*) = 0); - void set_shadow_type(shadow_type_enum type = SHADOW_CHARA); - void set_texture_color_coeff(vec4& value); - void set_texture_color_offset(vec4& value); - void set_texture_pattern(int32_t count = 0, texture_pattern_struct* value = 0); - void set_texture_specular_coeff(vec4& value); - void set_texture_specular_offset(vec4& value); - void set_texture_transform(int32_t count = 0, texture_transform_struct* value = 0); - void set_wet_param(float_t value = 0.0f); +struct imgfilter_batch_shader_data { + vec4 g_texture_lod; + vec4 g_color_scale; + vec4 g_color_offset; +}; + +struct glass_eye_batch_shader_data { + vec4 g_ellipsoid_radius; + vec4 g_ellipsoid_scale; + vec4 g_tex_model_param; + vec4 g_tex_offset; + vec4 g_eb_radius; + vec4 g_eb_tex_model_param; + vec4 g_fresnel; + vec4 g_refract1; + vec4 g_refract2; + vec4 g_iris_radius; + vec4 g_cornea_radius; + vec4 g_pupil_radius; + vec4 g_tex_scale; +}; + +struct obj_scene_shader_data { + vec4 g_irradiance_r_transforms[4]; + vec4 g_irradiance_g_transforms[4]; + vec4 g_irradiance_b_transforms[4]; + vec4 g_light_env_stage_diffuse; + vec4 g_light_env_stage_specular; + vec4 g_light_env_chara_diffuse; + vec4 g_light_env_chara_ambient; + vec4 g_light_env_chara_specular; + vec4 g_light_env_reflect_diffuse; + vec4 g_light_env_reflect_ambient; + vec4 g_light_env_proj_diffuse; + vec4 g_light_env_proj_specular; + vec4 g_light_env_proj_position; + vec4 g_light_stage_dir; + vec4 g_light_stage_diff; + vec4 g_light_stage_spec; + vec4 g_light_chara_dir; + vec4 g_light_chara_spec; + vec4 g_light_chara_luce; + vec4 g_light_chara_back; + vec4 g_light_face_diff; + vec4 g_chara_color_rim; + vec4 g_chara_color0; + vec4 g_chara_color1; + vec4 g_chara_f_dir; + vec4 g_chara_f_ambient; + vec4 g_chara_f_diffuse; + vec4 g_chara_tc_param; + vec4 g_fog_depth_color; + vec4 g_fog_height_params; //x=density, y=start, z=end, w=1/(end-start) + vec4 g_fog_height_color; + vec4 g_fog_bump_params; //x=density, y=start, z=end, w=1/(end-start) + vec4 g_fog_state_params; //x=density, y=start, z=end, w=1/(end-start) + vec4 g_normal_tangent_transforms[3]; + vec4 g_esm_param; + vec4 g_self_shadow_receivers[6]; + vec4 g_shadow_ambient; + vec4 g_shadow_ambient1; + vec4 g_framebuffer_size; + vec4 g_light_reflect_dir; + vec4 g_clip_plane; + vec4 g_npr_cloth_spec_color; + vec4 g_view[3]; + vec4 g_view_inverse[3]; + vec4 g_projection_view[4]; + vec4 g_view_position; + vec4 g_light_projection[4]; + + void set_g_irradiance_r_transforms(const mat4& mat); + void set_g_irradiance_g_transforms(const mat4& mat); + void set_g_irradiance_b_transforms(const mat4& mat); + void set_g_normal_tangent_transforms(const mat4& mat); + void set_g_self_shadow_receivers(int32_t index, const mat4& mat); + void set_g_light_projection(const mat4& mat); + + void set_projection_view(const mat4& view, const mat4& proj); +}; + +struct obj_batch_shader_data { + vec4 g_transforms[4]; + vec4 g_worlds[3]; + vec4 g_worlds_invtrans[3]; + vec4 g_worldview[3]; + vec4 g_worldview_inverse[3]; + vec4 g_joint[3]; + vec4 g_joint_inverse[3]; + vec4 g_texcoord_transforms[4]; + vec4 g_blend_color; + vec4 g_offset_color; + vec4 g_material_state_diffuse; + vec4 g_material_state_ambient; + vec4 g_material_state_emission; + vec4 g_material_state_shininess; + vec4 g_material_state_specular; + vec4 g_fresnel_coefficients; + vec4 g_texture_color_coefficients; + vec4 g_texture_color_offset; + vec4 g_texture_specular_coefficients; + vec4 g_texture_specular_offset; + vec4 g_shininess; + vec4 g_max_alpha; + vec4 g_morph_weight; + vec4 g_sss_param; + vec4 g_bump_depth; + vec4 g_intensity; + vec4 g_reflect_uv_scale; + vec4 g_texture_blend; + + void set_g_joint(const mat4& mat); + void set_g_texcoord_transforms(int32_t index, const mat4& mat); + + void set_transforms(const mat4& model, const mat4& view, const mat4& proj); +}; + +struct obj_skinning_shader_data { + vec4 g_joint_transforms[768]; +}; + +struct transparency_batch_shader_data { + vec4 g_opacity; +}; + +struct quad_shader_data { + vec4 g_texcoord_modifier; + vec4 g_texel_size; + vec4 g_color; + vec4 g_texture_lod; +}; + +struct sss_filter_gaussian_coef_shader_data { + vec4 g_param; + vec4 g_coef[64]; }; struct render_context { ::camera* camera; draw_state draw_state; - object_data object_data; - draw_pass draw_pass; + mdl::DispManager disp_manager; + rndr::RenderManager render_manager; face face; fog fog[FOG_MAX]; @@ -565,8 +814,32 @@ struct render_context { bool chara_refract; mat4 view_mat; + mat4 proj_mat; + mat4 vp_mat; + vec4 g_near_far; + mat4 matrix_buffer[MATRIX_BUFFER_COUNT]; + GLuint box_vao; + GLuint box_vbo; + + GL::UniformBuffer contour_coef_ubo; + GL::UniformBuffer contour_params_ubo; + GL::UniformBuffer filter_scene_ubo; + GL::UniformBuffer esm_filter_batch_ubo; + GL::UniformBuffer imgfilter_batch_ubo; + GL::UniformBuffer glass_eye_batch_ubo; + GL::UniformBuffer quad_ubo; + GL::UniformBuffer sss_filter_gaussian_coef_ubo; + GL::UniformBuffer transparency_batch_ubo; + + obj_scene_shader_data obj_scene; + obj_batch_shader_data obj_batch; + obj_skinning_shader_data obj_skinning; + GL::UniformBuffer obj_scene_ubo; + GL::UniformBuffer obj_batch_ubo; + GL::UniformBuffer obj_skinning_ubo; + render_context(); ~render_context(); @@ -581,8 +854,8 @@ struct render_context { }; struct shadow { - render_texture field_8[7]; - render_texture* field_158[3]; + render_texture field_8[8]; // Extra for buf + render_texture* field_158[4]; // Extra for buf float_t view_region; float_t range; vec3 view_point[2]; diff --git a/src/CRE/render_manager.cpp b/src/CRE/render_manager.cpp new file mode 100644 index 00000000..3be0fc4e --- /dev/null +++ b/src/CRE/render_manager.cpp @@ -0,0 +1,1545 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#include "render_manager.hpp" +#include "../KKdLib/mat.hpp" +#include "Glitter/glitter.hpp" +#include "light_param/fog.hpp" +#include "light_param/light.hpp" +#include "rob/rob.hpp" +#include "clear_color.hpp" +#include "gl_state.hpp" +#include "post_process.hpp" +#include "render_texture.hpp" +#include "shader_ft.hpp" +#include "static_var.hpp" +#include "task_effect.hpp" +#include "texture.hpp" + +struct texture_param { + GLint width; + GLint height; +}; + +static void blur_filter_apply(render_context* rctx, GLuint tex_0, GLuint tex_1, + blur_filter_mode filter, const vec2 res_scale, const vec4 scale, const vec4 offset); +static void draw_pass_shadow_begin_make_shadowmap(render_context* rctx, + shadow* shad, int32_t index, int32_t a3); +static void draw_pass_shadow_end_make_shadowmap(render_context* rctx, + shadow* shad, int32_t index, int32_t a3); +static void draw_pass_shadow_filter(render_context* rctx, render_texture* a1, render_texture* a2, + render_texture* a3, float_t sigma, float_t far_texel_offset, bool enable_lit_proj); +static void draw_pass_shadow_esm_filter(render_context* rctx, + render_texture* dst, render_texture* buf, render_texture* src); +static bool draw_pass_shadow_litproj(render_context* rctx, light_proj* litproj); +static void draw_pass_sss_contour(render_context* rctx, post_process* pp); +static void draw_pass_sss_filter(render_context* rctx, sss_data* a1); +static int32_t draw_pass_3d_get_translucent_count(render_context* rctx); +static void draw_pass_3d_shadow_reset(render_context* rctx); +static void draw_pass_3d_shadow_set(shadow* shad, render_context* rctx); +extern void draw_pass_3d_translucent(render_context* rctx, bool opaque_enable, + bool transparent_enable, bool translucent_enable, mdl::ObjType opaque, + mdl::ObjType transparent, mdl::ObjType translucent); +static int32_t draw_pass_3d_translucent_count_layers(render_context* rctx, + int32_t* alpha_array, mdl::ObjType opaque, + mdl::ObjType transparent, mdl::ObjType translucent); +static void draw_pass_3d_translucent_has_objects(render_context* rctx, bool* arr, mdl::ObjType type); +static void draw_pass_set_camera(render_context* rctx); +static void texture_params_get(GLuint tex_0, texture_param* tex_0_param, + GLuint tex_1, texture_param* tex_1_param, GLuint tex_2, texture_param* tex_2_param); +static void texture_params_restore(texture_param* tex_0_param, + texture_param* tex_1_param, texture_param* tex_2_param); + +extern bool draw_grid_3d; +extern void draw_pass_3d_grid(render_context* rctx); + +extern light_param_data_storage* light_param_data_storage_data; + +namespace rndr { + void RenderManager::render_all(render_context* rctx) { + static const int32_t ibl_texture_index[] = { + 9, 10, 11, 12, 13 + }; + + camera* cam = rctx->camera; + + for (int32_t i = 0; i < 5; i++) + gl_state_active_bind_texture_cube_map(ibl_texture_index[i], + light_param_data_storage_data->textures[i]); + + rctx->obj_scene.g_framebuffer_size = { + 1.0f / (float_t)rctx->post_process.render_width, + 1.0f / (float_t)rctx->post_process.render_height, + (float_t)rctx->post_process.render_width, + (float_t)rctx->post_process.render_height + }; + + gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + gl_state_enable_depth_test(); + gl_state_set_depth_func(GL_LEQUAL); + gl_state_set_depth_mask(GL_TRUE); + for (int32_t i = RND_PASSID_SHADOWMAP; i < RND_PASSID_NUM; i++) + RenderManager::render_single_pass((RenderPassID)i, rctx); + + rctx->disp_manager.check_vertex_arrays(); + gl_state_bind_vertex_array(0); + gl_state_disable_primitive_restart(); + gl_state_bind_uniform_buffer_base(0, 0); + gl_state_bind_uniform_buffer_base(1, 0); + gl_state_bind_uniform_buffer_base(2, 0); + gl_state_bind_uniform_buffer_base(3, 0); + gl_state_bind_uniform_buffer_base(4, 0); + } + + void RenderManager::render_single_pass(RenderPassID id, render_context* rctx) { + cpu_time[id] = 0.0; + gpu_time[id] = 0.0; + if (!pass_sw[id]) { + glGetError(); + return; + } + + render_pass_begin(); + switch (id) { + case RND_PASSID_SHADOWMAP: + pass_shadowmap(rctx); + break; + case RND_PASSID_SS_SSS: + pass_ss_sss(rctx); + break; + case RND_PASSID_REFLECT: + pass_reflect(rctx); + break; + case RND_PASSID_REFRACT: + pass_refract(rctx); + break; + case RND_PASSID_USER: + pass_user(rctx); + break; + case RND_PASSID_CLEAR: + pass_clear(rctx); + break; + case RND_PASSID_SPRITE_BG: + pass_sprite_bg(rctx); + break; + case RND_PASSID_ALL_3D: + pass_all_3d(rctx); + break; + case RND_PASSID_SHOW_VECTOR: + pass_show_vector(rctx); + break; + case RND_PASSID_POSTPROCESS: + pass_postprocess(rctx); + break; + case RND_PASSID_SPRITE_FG: + pass_sprite_fg(rctx); + break; + } + render_pass_end(id); + glGetError(); + } + + void RenderManager::render_pass_begin() { + if (sync_gpu) + glFinish(); + time.get_timestamp(); + } + + void RenderManager::render_pass_end(RenderPassID id) { + cpu_time[id] = time.calc_time(); + if (sync_gpu) { + time_struct t; + glFinish(); + gpu_time[id] = t.calc_time(); + } + else + gpu_time[id] = 0; + } + + void RenderManager::pass_shadowmap(render_context* rctx) { + rctx->camera->update_data(); + if (rctx->litproj->set(rctx)) { + rctx->obj_scene_ubo.WriteMapMemory(rctx->obj_scene); + + rctx->disp_manager.draw(mdl::OBJ_TYPE_OPAQUE_LITPROJ_0); + rctx->disp_manager.draw(mdl::OBJ_TYPE_TRANSPARENT_LITPROJ_0); + rctx->disp_manager.draw(mdl::OBJ_TYPE_TRANSLUCENT_LITPROJ_0); + + rctx->draw_state.shader_index = -1; + uniform_value[U0A] = 0; + draw_pass_shadow_filter(rctx, &rctx->litproj->shadow_texture[0], + &rctx->litproj->shadow_texture[1], 0, 1.5f, 0.01f, true); + + if (draw_pass_shadow_litproj(rctx, rctx->litproj)) { + draw_pass_set_camera(rctx); + + for (int32_t i = LIGHT_SET_MAIN; i < LIGHT_SET_MAX; i++) + rctx->light_set[i].data_set(rctx->face, (light_set_id)i); + + rctx->obj_scene_ubo.WriteMapMemory(rctx->obj_scene); + + rctx->disp_manager.draw(mdl::OBJ_TYPE_OPAQUE_LITPROJ_1); + rctx->disp_manager.draw(mdl::OBJ_TYPE_TRANSPARENT_LITPROJ_1); + rctx->disp_manager.obj_sort(&rctx->view_mat, mdl::OBJ_TYPE_TRANSLUCENT_LITPROJ_1, 1); + gl_state_set_blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl_state_enable_blend(); + rctx->disp_manager.draw(mdl::OBJ_TYPE_TRANSLUCENT_LITPROJ_1); + gl_state_disable_blend(); + rctx->draw_state.shader_index = -1; + gl_state_bind_framebuffer(0); + } + } + + rctx->obj_scene_ubo.WriteMapMemory(rctx->obj_scene); + + bool v3 = false; + int32_t v10[2]; + for (int32_t i = 0; i < 2; i++) { + v10[i] = rctx->disp_manager.get_obj_count((mdl::ObjType)(mdl::OBJ_TYPE_SHADOW_CHARA + i)); + if (v10[i]) + v3 = true; + } + + ::shadow* shad = shadow_ptr; + if (shadow && v3) { + int32_t v11[2]; + v11[0] = shad->field_200[0]; + v11[1] = shad->field_200[1]; + shad->field_158[0] = &shad->field_8[0]; + shad->field_158[1] = &shad->field_8[1]; + shad->field_158[2] = &shad->field_8[2]; + shad->field_158[3] = &shad->field_8[7]; // Extra for buf + + for (int32_t i = 0, j = 0; i < 2; i++) { + if (!v10[i]) + continue; + + draw_pass_shadow_begin_make_shadowmap(rctx, shad, v11[i], j); + rctx->disp_manager.draw((mdl::ObjType)(mdl::OBJ_TYPE_SHADOW_CHARA + v11[i])); + if (rctx->disp_manager.get_obj_count((mdl::ObjType)(mdl::OBJ_TYPE_SHADOW_OBJECT_CHARA + v11[i])) > 0) { + glColorMask(field_2F8 ? GL_TRUE : GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + rctx->disp_manager.draw((mdl::ObjType)(mdl::OBJ_TYPE_SHADOW_OBJECT_CHARA + i)); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } + draw_pass_shadow_end_make_shadowmap(rctx, shad, v11[i], j); + j++; + } + } + else { + shad->field_158[0] = &shad->field_8[0]; + shad->field_158[1] = &shad->field_8[1]; + shad->field_158[2] = &shad->field_8[2]; + shad->field_158[3] = &shad->field_8[7]; // Extra for buf + + for (int32_t i = 1; i < 3; i++) + for (int32_t j = 0; j < 4; j++) { + shad->field_158[i]->bind(j); + glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + } + } + gl_state_bind_framebuffer(0); + } + + void RenderManager::pass_ss_sss(render_context* rctx) { + ::sss_data* sss = &sss_data; + if (!sss->init || !sss->enable) + return; + + rctx->camera->update_data(); + post_process* pp = &rctx->post_process; + //if (pp->render_width > 1280.0) + // sss->npr_contour = false; + + if (sss->npr_contour) { + pp->rend_texture.bind(); + glViewport(0, 0, pp->render_width, pp->render_height); + } + else { + sss->textures[0].bind(); + glViewport(0, 0, 640, 360); + } + + glClearColor(sss->param.x, sss->param.y, sss->param.z, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + draw_pass_set_camera(rctx); + for (int32_t i = LIGHT_SET_MAIN; i < LIGHT_SET_MAX; i++) + rctx->light_set[i].data_set(rctx->face, (light_set_id)i); + + if (shadow) + draw_pass_3d_shadow_set(shadow_ptr, rctx); + else + draw_pass_3d_shadow_reset(rctx); + + rctx->obj_scene_ubo.WriteMapMemory(rctx->obj_scene); + + rctx->draw_state.shader_index = SHADER_FT_SSS_SKIN; + gl_state_enable_depth_test(); + gl_state_set_depth_func(GL_LEQUAL); + gl_state_set_depth_mask(GL_TRUE); + rctx->disp_manager.draw(mdl::OBJ_TYPE_SSS); + gl_state_disable_depth_test(); + rctx->draw_state.shader_index = -1; + draw_pass_3d_shadow_reset(rctx); + uniform_value[U_NPR] = 0; + + if (npr_param == 1) { + if (sss->enable && sss->npr_contour) { + uniform_value[U_NPR] = 1; + draw_pass_sss_contour(rctx, pp); + } + else if (npr) { + rctx->obj_scene_ubo.WriteMapMemory(rctx->obj_scene); + + pp->rend_texture.bind(); + glViewport(0, 0, pp->render_width, pp->render_height); + glClear(GL_DEPTH_BUFFER_BIT); + rctx->draw_state.shader_index = SHADER_FT_SSS_SKIN; + gl_state_enable_depth_test(); + gl_state_set_depth_mask(GL_TRUE); + rctx->disp_manager.draw(mdl::OBJ_TYPE_SSS); + gl_state_disable_depth_test(); + rctx->draw_state.shader_index = -1; + gl_state_bind_framebuffer(0); + uniform_value[U_NPR] = 1; + draw_pass_sss_contour(rctx, pp); + } + } + draw_pass_sss_filter(rctx, sss); + gl_state_bind_framebuffer(0); + } + + void RenderManager::pass_reflect(render_context* rctx) { + render_texture& refl_tex = rctx->render_manager.get_render_texture(0); + render_texture& refl_buf_tex = rctx->render_manager.get_render_texture(11); + refl_tex.bind(); + if (rctx->disp_manager.get_obj_count(mdl::OBJ_TYPE_REFLECT_OPAQUE) + || rctx->disp_manager.get_obj_count(mdl::OBJ_TYPE_REFLECT_TRANSPARENT) + || rctx->disp_manager.get_obj_count(mdl::OBJ_TYPE_REFLECT_TRANSLUCENT) + || rctx->disp_manager.get_obj_count(mdl::OBJ_TYPE_REFLECT_CHARA_OPAQUE) + || rctx->disp_manager.get_obj_count(mdl::OBJ_TYPE_REFLECT_CHARA_TRANSPARENT) + || rctx->disp_manager.get_obj_count(mdl::OBJ_TYPE_REFLECT_CHARA_TRANSLUCENT)) { + glViewport(0, 0, refl_tex.color_texture->width, refl_tex.color_texture->height); + + draw_pass_set_camera(rctx); + + for (int32_t i = LIGHT_SET_MAIN; i < LIGHT_SET_MAX; i++) + rctx->light_set[i].data_set(rctx->face, (light_set_id)i); + for (int32_t i = FOG_DEPTH; i < FOG_BUMP; i++) + rctx->fog[i].data_set((fog_id)i); + + rctx->obj_scene_ubo.WriteMapMemory(rctx->obj_scene); + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + rctx->draw_state.shader_index = SHADER_FT_S_REFL; + + light_set* set = &rctx->light_set[LIGHT_SET_MAIN]; + light_clip_plane clip_plane; + set->lights[LIGHT_SPOT].get_clip_plane(clip_plane); + uniform_value[U_REFLECT] = 2; + uniform_value[U_CLIP_PLANE] = clip_plane.data[1]; + + gl_state_enable_depth_test(); + gl_state_set_depth_func(GL_LEQUAL); + gl_state_set_depth_mask(GL_TRUE); + rctx->disp_manager.draw(mdl::OBJ_TYPE_REFLECT_OPAQUE, 0, field_31D); + if (reflect_type == STAGE_DATA_REFLECT_REFLECT_MAP) { + gl_state_enable_blend(); + gl_state_set_blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl_state_set_blend_equation(GL_FUNC_ADD); + rctx->disp_manager.draw(mdl::OBJ_TYPE_REFLECT_TRANSLUCENT); + gl_state_disable_blend(); + } + + if (reflect_type == STAGE_DATA_REFLECT_REFLECT_MAP) { + uniform_value[U_REFLECT] = field_31E; + uniform_value[U_CLIP_PLANE] = clip_plane.data[0]; + rctx->disp_manager.draw(mdl::OBJ_TYPE_REFLECT_CHARA_OPAQUE); + } + gl_state_disable_depth_test(); + uniform_value[U_REFLECT] = 0; + rctx->draw_state.shader_index = -1; + + for (int32_t i = reflect_blur_num, j = 0; i > 0; i--, j++) + if (j % 2) { + refl_tex.bind(); + blur_filter_apply(rctx, refl_tex.color_texture->tex, + refl_buf_tex.color_texture->tex, reflect_blur_filter, 1.0f, 1.0f, 0.0f); + } + else { + refl_buf_tex.bind(); + blur_filter_apply(rctx, refl_buf_tex.color_texture->tex, + refl_tex.color_texture->tex, reflect_blur_filter, 1.0f, 1.0f, 0.0f); + } + + if (reflect_blur_num % 2) + fbo::blit(refl_buf_tex.fbos[0], refl_tex.fbos[0], + 0, 0, refl_buf_tex.color_texture->width, refl_buf_tex.color_texture->height, + 0, 0, refl_tex.color_texture->width, refl_tex.color_texture->height, + GL_COLOR_BUFFER_BIT, GL_LINEAR); + } + else { + vec4 clear_color; + glGetFloatv(GL_COLOR_CLEAR_VALUE, (GLfloat*)&clear_color); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); + } + gl_state_bind_framebuffer(0); + } + + void RenderManager::pass_refract(render_context* rctx) { + render_texture& refract_texture = rctx->render_manager.get_render_texture(1); + refract_texture.bind(); + if (rctx->disp_manager.get_obj_count(mdl::OBJ_TYPE_REFRACT_OPAQUE) + || rctx->disp_manager.get_obj_count(mdl::OBJ_TYPE_REFRACT_TRANSPARENT) + || rctx->disp_manager.get_obj_count(mdl::OBJ_TYPE_REFRACT_TRANSLUCENT)) { + texture* refr_tex = refract_texture.color_texture; + glViewport(0, 0, refr_tex->width, refr_tex->height); + + draw_pass_set_camera(rctx); + + for (int32_t i = LIGHT_SET_MAIN; i < LIGHT_SET_MAX; i++) + rctx->light_set[i].data_set(rctx->face, (light_set_id)i); + for (int32_t i = FOG_DEPTH; i < FOG_BUMP; i++) + rctx->fog[i].data_set((fog_id)i); + + rctx->obj_scene_ubo.WriteMapMemory(rctx->obj_scene); + + vec4 clear_color; + glGetFloatv(GL_COLOR_CLEAR_VALUE, (GLfloat*)&clear_color); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); + + rctx->draw_state.shader_index = SHADER_FT_S_REFR; + gl_state_enable_depth_test(); + gl_state_set_depth_func(GL_LEQUAL); + gl_state_set_depth_mask(GL_TRUE); + rctx->disp_manager.draw(mdl::OBJ_TYPE_REFRACT_OPAQUE); + rctx->disp_manager.draw(mdl::OBJ_TYPE_REFRACT_TRANSPARENT); + rctx->disp_manager.draw(mdl::OBJ_TYPE_REFRACT_TRANSLUCENT); + gl_state_disable_depth_test(); + rctx->draw_state.shader_index = -1; + } + else { + vec4 clear_color; + glGetFloatv(GL_COLOR_CLEAR_VALUE, (GLfloat*)&clear_color); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); + } + gl_state_bind_framebuffer(0); + } + + void RenderManager::pass_user(render_context* rctx) { + for (draw_user& i : user) + if (i.func) + i.func(i.data); + } + + void RenderManager::pass_clear(render_context* rctx) { + if (true/*field_128*/) { + vec4 clear_color = get_clear_color(); + glClearBufferfv(GL_COLOR, 0, (GLfloat*)&clear_color); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + + post_process* pp = &rctx->post_process; + if (!sss_data.enable || !sss_data.npr_contour) { + pp->set_render_texture(); + + if (false/*sprite_manager_get_reqlist_count(2)*/) { + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClearDepth(1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + } + else { + vec4 clear_color = get_clear_color(); + glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); + glClearDepth(1.0f); + if (true/*field_128*/) + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + else + glClear(GL_DEPTH_BUFFER_BIT); + } + gl_state_bind_framebuffer(0); + } + else { + if (false/*sprite_manager_get_reqlist_count(2)*/) { + pp->set_render_texture(); + + vec4 clear_color = 0.0f; + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + gl_state_bind_framebuffer(0); + } + else if (false/*field_128*/) { + pp->set_render_texture(); + + vec4 clear_color = get_clear_color(); + glClearColor(clear_color.x, clear_color.y, clear_color.z, clear_color.w); + glClear(GL_COLOR_BUFFER_BIT); + gl_state_bind_framebuffer(0); + } + } + glGetError(); + } + + void RenderManager::pass_sprite_bg(render_context* rctx) { + if (!(false/*sprite_manager_get_reqlist_count(2)*/)) + return; + + post_process* pp = &rctx->post_process; + pp->set_render_texture(true); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + //rctx->camera->projection_aet_3d; + gl_state_set_depth_mask(GL_FALSE); + gl_state_disable_depth_test(); + gl_state_enable_blend(); + gl_state_disable_cull_face(); + //sprite_manager_draw(2, 1, pp->aa->mlaa_buffer[1].color_texture->tex); + gl_state_enable_cull_face(); + gl_state_disable_blend(); + gl_state_enable_depth_test(); + gl_state_set_depth_mask(GL_TRUE); + gl_state_bind_framebuffer(0); + glGetError(); + } + + void RenderManager::pass_all_3d(render_context* rctx) { + rctx->camera->update_data(); + rctx->post_process.set_render_texture(); + draw_pass_set_camera(rctx); + if (!sss_data.enable || !sss_data.npr_contour || draw_pass_3d_get_translucent_count(rctx)) + glClear(GL_DEPTH_BUFFER_BIT); + + gl_state_set_depth_func(GL_LEQUAL); + + for (int32_t i = LIGHT_SET_MAIN; i < LIGHT_SET_MAX; i++) + rctx->light_set[i].data_set(rctx->face, (light_set_id)i); + for (int32_t i = FOG_DEPTH; i < FOG_MAX; i++) + rctx->fog[i].data_set((fog_id)i); + + if (shadow) + draw_pass_3d_shadow_set(shadow_ptr, rctx); + else + draw_pass_3d_shadow_reset(rctx); + + rctx->obj_scene_ubo.WriteMapMemory(rctx->obj_scene); + + if (pass_sw[RND_PASSID_REFLECT] && reflect) { + render_texture& refl_tex = get_render_texture(0); + gl_state_active_bind_texture_2d(15, refl_tex.color_texture->tex); + uniform_value[U_WATER_REFLECT] = 1; + } + else + uniform_value[U_WATER_REFLECT] = 0; + + uniform_value[U12] = field_320 ? 1 : 0; + + if (effect_texture) + gl_state_active_bind_texture_2d(14, effect_texture->tex); + + gl_state_active_bind_texture_2d(16, sss_data.textures[1].color_texture->tex); + gl_state_active_texture(0); + + if (alpha_z_sort) { + rctx->disp_manager.obj_sort(&rctx->view_mat, mdl::OBJ_TYPE_TRANSLUCENT, 1); + rctx->disp_manager.obj_sort(&rctx->view_mat, mdl::OBJ_TYPE_TRANSLUCENT_NO_SHADOW, 2); + rctx->disp_manager.obj_sort(&rctx->view_mat, mdl::OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_1, 1); + rctx->disp_manager.obj_sort(&rctx->view_mat, mdl::OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_2, 1); + rctx->disp_manager.obj_sort(&rctx->view_mat, mdl::OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_3, 1); + rctx->disp_manager.obj_sort(&rctx->view_mat, mdl::OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_2_LOCAL, 1); + } + + if (opaque_z_sort) + rctx->disp_manager.obj_sort(&rctx->view_mat, mdl::OBJ_TYPE_OPAQUE, 0); + + if (draw_pass_3d[DRAW_PASS_3D_OPAQUE]) { + gl_state_enable_depth_test(); + gl_state_set_depth_mask(GL_TRUE); + rctx->disp_manager.draw(mdl::OBJ_TYPE_OPAQUE); + gl_state_disable_depth_test(); + } + + Glitter::glt_particle_manager->DispScenes(Glitter::DISP_OPAQUE); + + if (draw_grid_3d) + draw_pass_3d_grid(rctx); + + gl_state_enable_depth_test(); + gl_state_set_depth_mask(GL_TRUE); + if (draw_pass_3d[DRAW_PASS_3D_TRANSPARENT]) + rctx->disp_manager.draw(mdl::OBJ_TYPE_TRANSPARENT); + /*if (rctx->render_manager.field_120) + rctx->disp_manager.draw(mdl::OBJ_TYPE_TYPE_7);*/ + + rctx->post_process.exposure->get_exposure_chara_data(&rctx->post_process, rctx->camera); + + if (npr_param == 1) + pass_3d_contour(rctx); + + rctx->post_process.draw_lens_flare(rctx->camera); + //star_catalog_data.draw(); + + draw_pass_3d_translucent(rctx, + draw_pass_3d[DRAW_PASS_3D_OPAQUE], + draw_pass_3d[DRAW_PASS_3D_TRANSPARENT], + draw_pass_3d[DRAW_PASS_3D_TRANSLUCENT], + mdl::OBJ_TYPE_OPAQUE_ALPHA_ORDER_3, + mdl::OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_3, + mdl::OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_3); + + snow_particle_draw(); + rain_particle_draw(); + leaf_particle_draw(); + particle_draw(); + gl_state_disable_depth_test(); + + gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); + Glitter::glt_particle_manager->DispScenes(Glitter::DISP_ALPHA); + gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + /*if (npr_param == 1) { + gl_state_set_color_mask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + }*/ + + gl_state_enable_depth_test(); + + if (draw_pass_3d[DRAW_PASS_3D_TRANSLUCENT]) { + gl_state_enable_blend(); + gl_state_set_blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl_state_set_depth_mask(GL_FALSE); + rctx->disp_manager.draw(mdl::OBJ_TYPE_TRANSLUCENT_NO_SHADOW); + rctx->disp_manager.draw(mdl::OBJ_TYPE_TRANSLUCENT); + gl_state_disable_blend(); + } + + gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); // X + Glitter::glt_particle_manager->DispScenes(Glitter::DISP_TYPE_2); + gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + gl_state_set_depth_mask(GL_TRUE); + draw_pass_3d_translucent(rctx, + draw_pass_3d[DRAW_PASS_3D_OPAQUE], + draw_pass_3d[DRAW_PASS_3D_TRANSPARENT], + draw_pass_3d[DRAW_PASS_3D_TRANSLUCENT], + mdl::OBJ_TYPE_OPAQUE_ALPHA_ORDER_2, + mdl::OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_2, + mdl::OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_2); + gl_state_disable_depth_test(); + + gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); + Glitter::glt_particle_manager->DispScenes(Glitter::DISP_NORMAL); + gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + + gl_state_enable_depth_test(); + gl_state_set_depth_mask(GL_TRUE); + draw_pass_3d_translucent(rctx, + draw_pass_3d[DRAW_PASS_3D_OPAQUE], + draw_pass_3d[DRAW_PASS_3D_TRANSPARENT], + draw_pass_3d[DRAW_PASS_3D_TRANSLUCENT], + mdl::OBJ_TYPE_OPAQUE_ALPHA_ORDER_1, + mdl::OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_1, + mdl::OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_1); + + if (Glitter::glt_particle_manager->CheckHasLocalEffect()) { // X + camera* cam = rctx->camera; + double_t fov = cam->get_fov(); + cam->set_fov(32.2673416137695); + draw_pass_set_camera(rctx); + + rctx->obj_scene_ubo.WriteMapMemory(rctx->obj_scene); + + rctx->disp_manager.draw(mdl::OBJ_TYPE_OPAQUE_LOCAL); + rctx->disp_manager.draw(mdl::OBJ_TYPE_TRANSPARENT_LOCAL); + gl_state_enable_blend(); + rctx->disp_manager.draw(mdl::OBJ_TYPE_TRANSLUCENT_LOCAL); + gl_state_disable_blend(); + + draw_pass_3d_translucent(rctx, + draw_pass_3d[DRAW_PASS_3D_OPAQUE], + draw_pass_3d[DRAW_PASS_3D_TRANSPARENT], + draw_pass_3d[DRAW_PASS_3D_TRANSLUCENT], + mdl::OBJ_TYPE_OPAQUE_ALPHA_ORDER_2_LOCAL, + mdl::OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_2_LOCAL, + mdl::OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_2_LOCAL); + + gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_FALSE); + Glitter::glt_particle_manager->DispScenes(Glitter::DISP_LOCAL); + gl_state_set_color_mask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + cam->set_fov(fov); + draw_pass_set_camera(rctx); + } + + if (effect_texture) + gl_state_active_bind_texture_2d(14, 0); + + gl_state_disable_depth_test(); + + if (pass_sw[RND_PASSID_REFLECT] && reflect) + gl_state_active_bind_texture_2d(15, 0); + if (shadow) + draw_pass_3d_shadow_reset(rctx); + shader_opengl::unbind(); + pass_sprite_fg_surf(rctx); + gl_state_bind_framebuffer(0); + } + + void RenderManager::pass_show_vector(render_context* rctx) { + return; + /*if (!show_vector_flags) + return; + + rctx->camera->update_data(); + rctx->view_mat = rctx->camera->view; + rctx->post_process.set_render_texture(); + draw_pass_set_camera(rctx); + + rctx->obj_scene_ubo.WriteMapMemory(rctx->obj_scene); + + if (alpha_z_sort) { + rctx->disp_manager.obj_sort(&rctx->view_mat, mdl::OBJ_TYPE_TRANSLUCENT, 1); + rctx->disp_manager.obj_sort(&rctx->view_mat, mdl::OBJ_TYPE_TRANSLUCENT_NO_SHADOW, 2); + } + + if (opaque_z_sort) + rctx->disp_manager.obj_sort(&rctx->view_mat, mdl::OBJ_TYPE_OPAQUE, 0); + + //shaders_ft.env_vert_set(20, show_vector_length, show_vector_z_offset, 0.0f, 0.0f); + for (int32_t i = 1; i < 4; i++) { + if (!(show_vector_flags & (1 << (i - 1)))) + continue; + + if (draw_pass_3d[DRAW_PASS_3D_OPAQUE]) + draw_task_draw_objects_by_type_show_vector(rctx, mdl::OBJ_TYPE_OPAQUE, i); + if (draw_pass_3d[DRAW_PASS_3D_TRANSPARENT]) + draw_task_draw_objects_by_type_show_vector(rctx, mdl::OBJ_TYPE_TRANSPARENT, i); + if (draw_pass_3d[DRAW_PASS_3D_TRANSLUCENT]) { + draw_task_draw_objects_by_type_show_vector(rctx, mdl::OBJ_TYPE_TRANSLUCENT_NO_SHADOW, i); + draw_task_draw_objects_by_type_show_vector(rctx, mdl::OBJ_TYPE_TRANSLUCENT, i); + } + }*/ + } + + void RenderManager::pass_postprocess(render_context* rctx) { + rctx->camera->update_data(); + + texture* light_proj_tex = 0; + light_proj* litproj = rctx->litproj; + if (litproj && litproj->enable) { + light_set* set = &rctx->light_set[LIGHT_SET_MAIN]; + if (set->lights[LIGHT_PROJECTION].get_type() == LIGHT_SPOT + && texture_storage_get_texture(litproj->texture_id)) + light_proj_tex = litproj->draw_texture.color_texture; + } + + rctx->post_process.apply(rctx->camera, light_proj_tex, npr_param); + } + + void RenderManager::pass_sprite_fg(render_context* rctx) { + post_process* pp = &rctx->post_process; + + if (multisample && multisample_framebuffer) { + glBindFramebuffer(GL_FRAMEBUFFER, multisample_framebuffer); + gl_state_enable_multisample(); + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT); + } + + //rctx->camera->projection_aet_2d; + + gl_state_disable_depth_test(); + gl_state_enable_blend(); + gl_state_disable_cull_face(); + //sprite_manager_draw(0, 1, pp->aa->mlaa_buffer[1].color_texture->tex); + gl_state_enable_cull_face(); + gl_state_disable_blend(); + gl_state_enable_depth_test(); + + if (multisample && multisample_framebuffer) { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + gl_state_disable_multisample(); + gl_state_bind_read_framebuffer(multisample_framebuffer); + glBlitFramebuffer(0, 0, pp->sprite_width, pp->sprite_height, + 0, 0, pp->sprite_width, pp->sprite_height, GL_COLOR_BUFFER_BIT, GL_NEAREST); + gl_state_bind_read_framebuffer(0); + } + glGetError(); + } + + void RenderManager::pass_3d_contour(render_context* rctx) { + render_texture* rt = &rctx->post_process.rend_texture; + render_texture* contour_rt = &rctx->post_process.sss_contour_texture; + + gl_state_enable_blend(); + gl_state_set_blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl_state_set_blend_equation(GL_FUNC_ADD); + gl_state_disable_depth_test(); + + contour_params_shader_data shader_data = {}; + shader_data.g_near_far = rctx->g_near_far; + rctx->contour_params_ubo.WriteMapMemory(shader_data); + + shaders_ft.set_opengl_shader(SHADER_FT_CONTOUR_NPR); + gl_state_active_bind_texture_2d(16, contour_rt->color_texture->tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl_state_active_bind_texture_2d(17, contour_rt->depth_texture->tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + gl_state_active_bind_texture_2d(14, rt->depth_texture->tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + rctx->contour_params_ubo.Bind(2); + render_texture::draw(&shaders_ft); + + if (effect_texture) + gl_state_active_bind_texture_2d(14, effect_texture->tex); + gl_state_enable_depth_test(); + gl_state_disable_blend(); + } + + void RenderManager::pass_sprite_fg_surf(render_context* rctx) { + if (!(false/*sprite_manager_get_reqlist_count(1)*/)) + return; + + //rctx->camera->projection_aet_2d; + gl_state_set_depth_mask(GL_FALSE); + gl_state_disable_depth_test(); + gl_state_disable_depth_test(); + gl_state_enable_blend(); + gl_state_disable_cull_face(); + //sprite_manager_draw(1, 1, rctx->post_process.aa->temp_buffer.color_texture); + } +} + +void image_filter_scale(render_context* rctx, GLuint dst, GLuint src, const vec4 scale) { + if (!dst || !src) + return; + + texture_param tex_params[2]; + texture_params_get(dst, &tex_params[0], src, &tex_params[1], 0, 0); + + filter_scene_shader_data filter_scene = {}; + filter_scene.g_transform = { 1.0f, 1.0f, 0.0f, 0.0f }; + filter_scene.g_texcoord = { 1.0f, 1.0f, 0.0f, 0.0f }; + rctx->filter_scene_ubo.WriteMapMemory(filter_scene); + + imgfilter_batch_shader_data imgfilter_batch = {}; + imgfilter_batch.g_color_scale = 1.0f; + imgfilter_batch.g_color_offset = 0.0f; + imgfilter_batch.g_texture_lod = 0.0f; + rctx->imgfilter_batch_ubo.WriteMapMemory(imgfilter_batch); + + uniform_value[U_IMAGE_FILTER] = 5; + shaders_ft.set_opengl_shader(SHADER_FT_IMGFILT); + rctx->filter_scene_ubo.Bind(0); + rctx->imgfilter_batch_ubo.Bind(1); + gl_state_active_bind_texture_2d(0, src); + render_texture::draw_custom(); + texture_params_restore(&tex_params[0], &tex_params[1], 0); +} + +static void draw_pass_shadow_begin_make_shadowmap(render_context* rctx, + shadow* shad, int32_t index, int32_t a3) { + texture* tex = shad->field_158[0]->color_texture; + shad->field_158[0]->bind(); + glViewport(0, 0, tex->width, tex->height); + glScissor(0, 0, tex->width, tex->height); + gl_state_enable_depth_test(); + gl_state_enable_scissor_test(); + glClearColor(1.0, 1.0, 1.0, 1.0); + if (!a3) + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + else if (shad->field_2F5) + glClear(GL_COLOR_BUFFER_BIT); + glScissor(1, 1, tex->width - 2, tex->height - 2); + + float_t offset = 0.0f; + float_t range = shad->view_region * shad->range; + vec3* interest; + vec3* view_point; + if (shad->field_2F5) { + offset = -0.5f; + if (index) + offset = 0.5f; + + interest = &shad->interest[index]; + view_point = &shad->view_point[index]; + } + else { + interest = &shad->interest_shared; + view_point = &shad->view_point_shared; + } + + mat4 temp; + mat4_translate(offset, 0.0f, 0.0f, &temp); + + mat4 proj; + mat4_ortho(-range, range, -range, range, shad->z_near, shad->z_far, &proj); + mat4_mult(&proj, &temp, &rctx->proj_mat); + mat4_look_at(view_point, interest, &rctx->view_mat); + + rctx->draw_state.shader_index = SHADER_FT_SIL; + uniform_value[U0A] = 0; +} + +static void draw_pass_shadow_end_make_shadowmap(render_context* rctx, + shadow* shad, int32_t index, int32_t a3) { + gl_state_disable_depth_test(); + gl_state_disable_scissor_test(); + + rctx->draw_state.shader_index = -1; + if (a3 == shad->field_2EC - 1) { + draw_pass_shadow_filter(rctx, &shad->field_8[3], &shad->field_8[4], shad->field_158[0], + shad->field_2DC, shad->field_2E0 / (shad->field_208 * 2.0f), false); + draw_pass_shadow_esm_filter(rctx, &shad->field_8[5], &shad->field_8[6], &shad->field_8[3]); + } + + render_texture* rend_tex = shad->field_158[1 + index]; + render_texture* rend_buf_tex = shad->field_158[3]; + rend_tex->bind(); + + render_texture* src = shad->field_158[0]; + image_filter_scale(rctx, rend_tex->color_texture->tex, src->color_texture->tex, 1.0f); + + if (shad->blur_filter_enable[index]) { + for (int32_t i = shad->near_blur, j = 0; i > 0; i--, j++) + if (j % 2) { + rend_tex->bind(); + blur_filter_apply(rctx, rend_tex->color_texture->tex, + rend_buf_tex->color_texture->tex, shad->blur_filter, 1.0f, 1.0f, 0.0f); + } + else { + rend_buf_tex->bind(); + blur_filter_apply(rctx, rend_buf_tex->color_texture->tex, + rend_tex->color_texture->tex, shad->blur_filter, 1.0f, 1.0f, 0.0f); + } + + if (shad->near_blur % 2) + fbo::blit(rend_buf_tex->fbos[0], rend_tex->fbos[0], + 0, 0, rend_buf_tex->color_texture->width, rend_buf_tex->color_texture->height, + 0, 0, rend_tex->color_texture->width, rend_tex->color_texture->height, + GL_COLOR_BUFFER_BIT, GL_LINEAR); + } + else { + rend_tex->bind(); + glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + } + + gl_state_bind_framebuffer(0); + gl_state_bind_texture_2d(rend_tex->color_texture->tex); + glGenerateMipmap(GL_TEXTURE_2D); + gl_state_bind_texture_2d(0); +} + +static void draw_pass_shadow_filter(render_context* rctx, render_texture* a1, render_texture* a2, + render_texture* a3, float_t sigma, float_t far_texel_offset, bool enable_lit_proj) { + GLuint v7 = a1->color_texture->tex; + GLuint v9 = a2->color_texture->tex; + GLuint v11 = v7; + if (a3) + v11 = a3->depth_texture->tex; + + if (!v7 || !v9 || !v11) + return; + + texture_param tex_params[2]; + texture_params_get(v7, &tex_params[0], v9, &tex_params[1], 0, 0); + if (tex_params[0].width != tex_params[1].width + || tex_params[0].height != tex_params[1].height) + return; + + filter_scene_shader_data filter_scene = {}; + filter_scene.g_transform = 0.0f; + filter_scene.g_texcoord = { 1.0f, 1.0f, 0.0f, 0.0f }; + rctx->filter_scene_ubo.WriteMapMemory(filter_scene); + + esm_filter_batch_shader_data esm_filter_batch = {}; + esm_filter_batch.g_params = { 1.0f / (float_t)tex_params[0].width, 0.0f, far_texel_offset, far_texel_offset }; + double_t v6 = 1.0 / (sqrt(M_PI * 2.0) * sigma); + double_t v8 = -1.0 / (2.0 * sigma * sigma); + for (int32_t i = 0; i < 8; i++) + ((float_t*)esm_filter_batch.g_gauss)[i] = (float_t)(exp((double_t)((ssize_t)i * i) * v8) * v6); + + uniform_value[U_LIGHT_PROJ] = enable_lit_proj ? 1 : 0; + shaders_ft.set_opengl_shader(SHADER_FT_ESMGAUSS); + rctx->filter_scene_ubo.Bind(0); + rctx->esm_filter_batch_ubo.Bind(1); + + a2->bind(); + esm_filter_batch.g_params = { 1.0f / (float_t)tex_params[0].width, 0.0f, far_texel_offset, far_texel_offset }; + rctx->esm_filter_batch_ubo.WriteMapMemory(esm_filter_batch); + + gl_state_active_bind_texture_2d(0, v11); + if (a3) { + GLint swizzle[] = { GL_RED, GL_RED, GL_RED, GL_ONE }; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle); + } + else { + GLint swizzle[] = { GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA }; + glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle); + } + render_texture::draw_custom(); + + a1->bind(); + + esm_filter_batch.g_params = { 0.0f, 1.0f / (float_t)tex_params[0].height, far_texel_offset, far_texel_offset }; + rctx->esm_filter_batch_ubo.WriteMapMemory(esm_filter_batch); + + gl_state_active_bind_texture_2d(0, v9); + render_texture::draw_custom(); + shader_opengl::unbind(); + texture_params_restore(&tex_params[0], &tex_params[1], 0); +} + +static void draw_pass_shadow_esm_filter(render_context* rctx, + render_texture* dst, render_texture* buf, render_texture* src) { + GLuint dst_tex = dst->color_texture->tex; + GLuint buf_tex = buf->color_texture->tex; + GLuint src_tex = src->color_texture->tex; + if (!dst_tex || !buf_tex || !src_tex) + return; + + filter_scene_shader_data filter_scene = {}; + filter_scene.g_transform = 0.0f; + filter_scene.g_texcoord = { 1.0f, 1.0f, 0.0f, 0.0f }; + rctx->filter_scene_ubo.WriteMapMemory(filter_scene); + + esm_filter_batch_shader_data esm_filter_batch = {}; + esm_filter_batch.g_gauss[0] = 0.0f; + esm_filter_batch.g_gauss[1] = 0.0f; + + rctx->filter_scene_ubo.Bind(0); + rctx->esm_filter_batch_ubo.Bind(1); + + texture_param tex_params[3]; + texture_params_get(dst_tex, &tex_params[0], src_tex, &tex_params[1], buf_tex, &tex_params[2]); + + buf->bind(); + esm_filter_batch.g_params = { 1.0f / (float_t)tex_params[1].width, + 1.0f / (float_t)tex_params[1].height, 0.0f, 0.0f }; + rctx->esm_filter_batch_ubo.WriteMapMemory(esm_filter_batch); + + uniform_value[U_ESM_FILTER] = 0; + shaders_ft.set_opengl_shader(SHADER_FT_ESMFILT); + gl_state_active_bind_texture_2d(0, src_tex); + render_texture::draw(&shaders_ft); + + dst->bind(); + esm_filter_batch.g_params = { 0.75f / (float_t)tex_params[2].width, + 0.75f / (float_t)tex_params[2].height, 0.0f, 0.0f }; + rctx->esm_filter_batch_ubo.WriteMapMemory(esm_filter_batch); + + uniform_value[U_ESM_FILTER] = 1; + shaders_ft.set_opengl_shader(SHADER_FT_ESMFILT); + gl_state_active_bind_texture_2d(0, buf_tex); + render_texture::draw(&shaders_ft); + texture_params_restore(&tex_params[0], &tex_params[1], &tex_params[2]); +} + +static bool draw_pass_shadow_litproj(render_context* rctx, light_proj* litproj) { + if (!litproj) + return false; + + texture* tex = texture_storage_get_texture(litproj->texture_id); + if (!tex) + return false; + + litproj->draw_texture.bind(); + + glViewport(0, 0, litproj->draw_texture.color_texture->width, + litproj->draw_texture.color_texture->height); + gl_state_enable_depth_test(); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClearDepthf(1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + if (!light_proj::set_mat(rctx, 0)) { + gl_state_bind_framebuffer(0); + return false; + } + + static const vec4 border_color = 0.0f; + rctx->draw_state.shader_index = SHADER_FT_LITPROJ; + gl_state_active_bind_texture_2d(17, tex->tex); + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, (GLfloat*)&border_color); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + gl_state_active_bind_texture_2d(18, litproj->shadow_texture[0].color_texture->tex); + glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, (GLfloat*)&border_color); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + gl_state_active_texture(0); + return true; +} + +static void draw_pass_sss_contour(render_context* rctx, post_process* pp) { + pp->sss_contour_texture.bind(); + gl_state_enable_depth_test(); + gl_state_set_depth_func(GL_ALWAYS); + gl_state_set_depth_mask(GL_TRUE); + glViewport(0, 0, pp->render_width, pp->render_height); + gl_state_active_bind_texture_2d(0, pp->rend_texture.color_texture->tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + gl_state_active_bind_texture_2d(1, pp->rend_texture.depth_texture->tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + gl_state_active_texture(0); + + camera* cam = rctx->camera; + float_t v3 = 1.0f / tanf((float_t)(cam->fov_rad * 0.5)); + + vec3 direction = cam->interest - cam->view_point; + float_t length = vec3::length(direction); + float_t v7 = direction.y; + if (length != 0.0f) + v7 /= length; + + float_t v9 = fabsf(v7) - 0.1f; + if (v9 < 0.0f) + v9 = 0.0f; + + contour_coef_shader_data shader_data = {}; + shader_data.g_contour = { v9 * 0.004f + 0.0027f, 0.003f, v3 * 0.35f, 0.0008f }; + shader_data.g_near_far = rctx->g_near_far; + rctx->contour_coef_ubo.WriteMapMemory(shader_data); + + shaders_ft.set_opengl_shader(SHADER_FT_CONTOUR); + rctx->contour_coef_ubo.Bind(2); + render_texture::draw_quad(&shaders_ft, pp->render_width, pp->render_height); +} + +static void draw_pass_sss_filter_calc_coef(double_t a1, size_t a2, double_t a3, size_t a4, + const double_t* a5, const double_t* a6, const double_t* a7, const double_t* a8, vec4 params[64]) { + if (a2 > 8) + return; + + for (size_t i = a4; i; i--) { + float_t* v20 = (float_t*)params; + double_t v56[3]; + v56[0] = *a6; + v56[1] = *a7; + v56[2] = *a8; + + for (size_t j = 0; j < 3; j++) { + double_t v22 = 0.0; + double_t v23 = 0.0; + double_t v54[8]; + double_t v24 = 1.0 / (a3 * v56[j]); + v24 *= v24; + for (size_t k = 0; k < a2; k++) { + double_t v25 = exp(-0.5 * (v23 * v23) * v24); + v54[k] = v25; + v23 += a1; + v22 += v25; + } + + double_t v27 = 1.0 / v22; + for (size_t k = 0; k < a2; k++) + v54[k] *= v27; + + float_t* v35 = v20; + for (size_t k = 0; k < a2; k++) { + double_t v37 = v54[k] * *a5; + for (size_t v36 = 0; v36 < a2; v36++) { + *v35 += (float_t)(v37 * v54[v36]); + v35 += 4; + } + } + v20++; + } + a5++; + a6++; + a7++; + a8++; + } +} + +static void draw_pass_sss_filter(render_context* rctx, sss_data* a1) { + const int32_t sss_count = 6; + vec3 interest = rctx->camera->interest; + vec3 view_point = rctx->camera->view_point; + + vec3 chara_position[2]; + chara_position[0] = 0.0f; + chara_position[1] = 0.0f; + float_t chara_distance[2]; + for (int32_t i = 0; i < 2; i++) { + chara_position[i] = interest; + chara_distance[i] = 999999.0f; + rob_chara_bone_data* v16 = rob_chara_array[i].bone_data; + if (rob_chara_pv_data_array[i].type != ROB_CHARA_TYPE_NONE && rob_chara_array[i].data.field_0 & 1) { + mat4* mat = rob_chara_bone_data_get_mats_mat(v16, MOTION_BONE_N_HARA_CP); + if (mat) { + mat4_get_translation(mat, &chara_position[i]); + chara_distance[i] = vec3::distance(view_point, chara_position[i]); + } + } + } + + vec3 closest_chara_position; + if (chara_distance[0] > chara_distance[1]) { + closest_chara_position = chara_position[1]; + chara_position[0] = chara_position[1]; + } + else + closest_chara_position = chara_position[0]; + + float_t length = vec3::distance(interest, closest_chara_position); + if (length > 1.25f) + interest = chara_position[0]; + + float_t v29 = vec3::distance(view_point, interest); + if (v29 < 0.25f) + v29 = 0.25f; + float_t v31 = tanf((float_t)(rctx->camera->fov_rad * 0.5)) * 5.0f; + if (v31 < 0.25f) + v31 = 0.25f; + float_t v32 = v31 * v29; + float_t v34 = 1.0f / clamp_def(v32, 0.25f, 100.0f); + + gl_state_active_texture(0); + if (a1->npr_contour) { + a1->textures[0].bind(); + glViewport(0, 0, 640, 360); + post_process* pp = &rctx->post_process; + uniform_value[U_REDUCE] = 0; + shaders_ft.set_opengl_shader(SHADER_FT_REDUCE); + gl_state_bind_texture_2d(pp->rend_texture.color_texture->tex); + render_texture::draw_quad(&shaders_ft, 640, 360, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f); + } + a1->textures[2].bind(); + glViewport(0, 0, 320, 180); + uniform_value[U_SSS_FILTER] = 0; + shaders_ft.set_opengl_shader(SHADER_FT_SSS_FILT); + gl_state_bind_texture_2d(a1->textures[0].color_texture->tex); + render_texture::draw_quad(&shaders_ft, 640, 360, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f); + + sss_filter_gaussian_coef_shader_data shader_data = {}; + shader_data.g_param = { (float_t)(sss_count - 1), 0.0f, 1.0f, 1.0f }; + + const double_t a5[] = { 0.4, 0.3, 0.3 }; + const double_t a6[] = { 1.0, 2.0, 5.0 }; + const double_t a7[] = { 0.2, 0.4, 1.2 }; + const double_t a8[] = { 0.3, 0.7, 2.0 }; + draw_pass_sss_filter_calc_coef(1.0, sss_count, v34, 3, a5, a6, a7, a8, shader_data.g_coef); + + rctx->sss_filter_gaussian_coef_ubo.WriteMapMemory(shader_data); + a1->textures[1].bind(); + glViewport(0, 0, 320, 180); + uniform_value[U_SSS_FILTER] = 3; + shaders_ft.set_opengl_shader(SHADER_FT_SSS_FILT); + gl_state_bind_texture_2d(a1->textures[2].color_texture->tex); + rctx->sss_filter_gaussian_coef_ubo.Bind(1); + render_texture::draw_quad(&shaders_ft, 320, 180, 1.0f, 1.0f, 0.96f, 1.0f, 0.0f); + gl_state_bind_texture_2d(0); +} + +void sub_1404A9350(post_process* a1, bool a2) { + if (a2) { + a1->aet_back_texture.bind(); + a1->aet_back = 1; + } + else + a1->rend_texture.bind(); + glViewport(0, 0, a1->render_width, a1->render_height); +} + +static int32_t draw_pass_3d_get_translucent_count(render_context* rctx) { + int32_t count = 0; + count += rctx->disp_manager.get_obj_count(mdl::OBJ_TYPE_OPAQUE_ALPHA_ORDER_1); + count += rctx->disp_manager.get_obj_count(mdl::OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_1); + count += rctx->disp_manager.get_obj_count(mdl::OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_1); + count += rctx->disp_manager.get_obj_count(mdl::OBJ_TYPE_OPAQUE_ALPHA_ORDER_2); + count += rctx->disp_manager.get_obj_count(mdl::OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_2); + count += rctx->disp_manager.get_obj_count(mdl::OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_2); + count += rctx->disp_manager.get_obj_count(mdl::OBJ_TYPE_OPAQUE_ALPHA_ORDER_3); + count += rctx->disp_manager.get_obj_count(mdl::OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_3); + count += rctx->disp_manager.get_obj_count(mdl::OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_3); + count += rctx->disp_manager.get_obj_count(mdl::OBJ_TYPE_OPAQUE_ALPHA_ORDER_2_LOCAL); + count += rctx->disp_manager.get_obj_count(mdl::OBJ_TYPE_TRANSPARENT_ALPHA_ORDER_2_LOCAL); + count += rctx->disp_manager.get_obj_count(mdl::OBJ_TYPE_TRANSLUCENT_ALPHA_ORDER_2_LOCAL); + return count; +} + +static void draw_pass_3d_shadow_reset(render_context* rctx) { + gl_state_active_bind_texture_2d(6, 0); + gl_state_active_bind_texture_2d(7, 0); + rctx->obj_scene.set_g_self_shadow_receivers(0, mat4_identity); + rctx->obj_scene.set_g_self_shadow_receivers(1, mat4_identity); + rctx->draw_state.self_shadow = false; + rctx->draw_state.light = false; +} + +static void draw_pass_3d_shadow_set(shadow* shad, render_context* rctx) { + if (shad->self_shadow && shad->field_2EC > 0) { + gl_state_active_bind_texture_2d(19, shad->field_8[3].color_texture->tex); + gl_state_active_bind_texture_2d(20, shad->field_8[5].color_texture->tex); + gl_state_active_texture(0); + float_t esm_param = (shad->field_2D8 * shad->field_208 * 2.0f) * 1.442695f; + rctx->obj_scene.g_esm_param = { esm_param, 0.0f, 0.0f, 0.0f }; + rctx->draw_state.self_shadow = true; + } + else + rctx->draw_state.self_shadow = false; + + if (shad->field_2EC > 0) { + rctx->draw_state.light = true; + uniform_value[U_LIGHT_1] = shad->field_2EC > 1 ? 1 : 0; + + float_t range = shad->view_region * shad->range; + for (int32_t i = 0; i < 2; i++) { + float_t v6 = 0.0f; + vec3* interest; + vec3* view_point; + if (shad->field_2F5) { + v6 = i ? 0.5f : -0.5f; + + interest = &shad->interest[i]; + view_point = &shad->view_point[i]; + } + else { + interest = &shad->interest_shared; + view_point = &shad->view_point_shared; + } + + mat4 temp; + mat4_translate(0.5f, 0.5f, 0.5f, &temp); + mat4_scale_rot(&temp, 0.5f, 0.5f, 0.5f, &temp); + mat4_translate_mult(&temp, v6, 0.0f, 0.0f, &temp); + + mat4 proj; + mat4_ortho(-range, range, -range, range, shad->z_near, shad->z_far, &proj); + mat4_mult(&proj, &temp, &proj); + + mat4 view; + mat4_look_at(view_point, interest, &view); + mat4_mult(&view, &proj, &view); + rctx->obj_scene.set_g_self_shadow_receivers(i, view); + } + + for (int32_t i = 0, j = 0; i < 2; i++) { + if (!shad->field_2F0[i]) + continue; + + gl_state_active_bind_texture_2d(6 + j, shad->field_158[1 + i]->color_texture->tex); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 16.0f); + j++; + } + gl_state_active_texture(0); + + light_set* set = &rctx->light_set[LIGHT_SET_MAIN]; + light_data* data = &set->lights[LIGHT_SHADOW]; + + vec4 ambient; + if (data->get_type() == LIGHT_PARALLEL) + data->get_ambient(ambient); + else { + ambient.x = shad->ambient; + ambient.y = shad->ambient; + ambient.z = shad->ambient; + } + + rctx->obj_scene.g_shadow_ambient = { ambient.x, ambient.y, ambient.z, 1.0f }; + rctx->obj_scene.g_shadow_ambient1 = { 1.0f - ambient.x, 1.0f - ambient.y, 1.0f - ambient.z, 0.0f }; + } + else { + rctx->draw_state.light = false; + + for (int32_t i = 0; i < 2; i++) + gl_state_active_bind_texture_2d(6 + i, shad->field_158[1 + i]->color_texture->tex); + gl_state_active_texture(0); + + rctx->obj_scene.g_shadow_ambient = 1.0f; + rctx->obj_scene.g_shadow_ambient1 = 0.0f; + } +} + +static void draw_pass_3d_translucent(render_context* rctx, bool opaque_enable, + bool transparent_enable, bool translucent_enable, mdl::ObjType opaque, + mdl::ObjType transparent, mdl::ObjType translucent) { + if (rctx->disp_manager.get_obj_count(opaque) < 1 + && rctx->disp_manager.get_obj_count(transparent) < 1 + && rctx->disp_manager.get_obj_count(translucent) < 1) + return; + + post_process* pp = &rctx->post_process; + + int32_t alpha_array[256]; + int32_t count = draw_pass_3d_translucent_count_layers(rctx, + alpha_array, opaque, transparent, translucent); + for (int32_t i = 0; i < count; i++) { + int32_t alpha = alpha_array[i]; + fbo::blit(pp->rend_texture.fbos[0], pp->transparency_texture.fbos[0], + 0, 0, pp->render_width, pp->render_height, + 0, 0, pp->render_width, pp->render_height, + GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST); + + if (opaque_enable && rctx->disp_manager.get_obj_count(opaque)) + rctx->disp_manager.draw(opaque, alpha); + if (transparent_enable && rctx->disp_manager.get_obj_count(transparent)) + rctx->disp_manager.draw(transparent, alpha); + if (translucent_enable && rctx->disp_manager.get_obj_count(translucent)) { + gl_state_enable_blend(); + rctx->disp_manager.draw(translucent, alpha); + gl_state_disable_blend(); + } + + transparency_batch_shader_data shader_data = {}; + shader_data.g_opacity = { (float_t)alpha * (float_t)(1.0 / 255.0), 0.0f, 0.0f, 0.0f }; + rctx->transparency_batch_ubo.WriteMapMemory(shader_data); + + pp->buf_texture.bind(); + shaders_ft.set_opengl_shader(SHADER_FT_TRANSPARENCY); + rctx->transparency_batch_ubo.Bind(0); + gl_state_active_bind_texture_2d(0, pp->transparency_texture.color_texture->tex); + gl_state_active_bind_texture_2d(1, pp->rend_texture.color_texture->tex); + render_texture::draw_custom(); + + fbo::blit(pp->buf_texture.fbos[0], pp->rend_texture.fbos[0], + 0, 0, pp->render_width, pp->render_height, + 0, 0, pp->render_width, pp->render_height, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + } +} + +static int32_t draw_pass_3d_translucent_count_layers(render_context* rctx, + int32_t* alpha_array, mdl::ObjType opaque, + mdl::ObjType transparent, mdl::ObjType translucent) { + bool arr[256] = { 0 }; + + draw_pass_3d_translucent_has_objects(rctx, arr, opaque); + draw_pass_3d_translucent_has_objects(rctx, arr, transparent); + draw_pass_3d_translucent_has_objects(rctx, arr, translucent); + + int32_t count = 0; + for (int32_t i = 0xFF; i >= 1; i--) + if (arr[i]) { + count++; + *alpha_array++ = i; + } + return count; +} + +static void draw_pass_3d_translucent_has_objects(render_context* rctx, bool* arr, mdl::ObjType type) { + std::vector& vec = rctx->disp_manager.obj[type]; + for (mdl::ObjData*& i : vec) + if (i->kind == mdl::OBJ_KIND_TRANSLUCENT) { + int32_t alpha = (int32_t)(i->args.sub_mesh.blend_color.w * 255.0f); + alpha = clamp_def(alpha, 0, 255); + arr[alpha] = true; + } +} + +static void draw_pass_set_camera(render_context* rctx) { + camera* cam = rctx->camera; + cam->update_data(); + rctx->view_mat = cam->view; + rctx->proj_mat = cam->projection; + rctx->vp_mat = cam->view_projection; + rctx->obj_scene.set_projection_view(cam->view, cam->projection); + cam->get_view_point(rctx->obj_scene.g_view_position); + + rctx->g_near_far = { + (float_t)(cam->max_distance / (cam->max_distance - cam->min_distance)), + (float_t)(-(cam->max_distance * cam->min_distance) / (cam->max_distance - cam->min_distance)), + 0.0f, 0.0f + }; +} + +static void texture_params_get(GLuint tex_0, texture_param* tex_0_param, + GLuint tex_1, texture_param* tex_1_param, GLuint tex_2, texture_param* tex_2_param) { + gl_state_disable_depth_test(); + if (tex_0_param) { + tex_0_param->width = 0; + tex_0_param->height = 0; + if (tex_0) { + gl_state_bind_texture_2d(tex_0); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &tex_0_param->width); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &tex_0_param->height); + } + } + + if (tex_1_param) { + tex_1_param->width = 0; + tex_1_param->height = 0; + if (tex_1) { + gl_state_bind_texture_2d(tex_1); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &tex_1_param->width); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &tex_1_param->height); + } + } + + if (tex_2_param) { + tex_2_param->width = 0; + tex_2_param->height = 0; + if (tex_2) { + gl_state_bind_texture_2d(tex_2); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &tex_2_param->width); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &tex_2_param->height); + } + } + gl_state_bind_texture_2d(0); + + if (tex_0_param) + glViewport(0, 0, tex_0_param->width, tex_0_param->height); +} + +static void texture_params_restore(texture_param* tex_0_param, + texture_param* tex_1_param, texture_param* tex_2_param) { + for (int32_t i = 0; i < 4; i++) + gl_state_active_bind_texture_2d(i, 0); +} + +static void blur_filter_apply(render_context* rctx, GLuint dst, GLuint src, + blur_filter_mode filter, const vec2 res_scale, const vec4 scale, const vec4 offset) { + if (!dst || !src) + return; + + texture_param tex_params[2]; + texture_params_get(dst, &tex_params[0], src, &tex_params[1], 0, 0); + + filter_scene_shader_data filter_scene = {}; + float_t w = res_scale.x / (float_t)tex_params[1].width; + float_t h = res_scale.y / (float_t)tex_params[1].height; + filter_scene.g_transform = { w, h, 0.0f, 0.0f }; + filter_scene.g_texcoord = { 1.0f, 1.0f, 0.0f, 0.0f }; + rctx->filter_scene_ubo.WriteMapMemory(filter_scene); + + imgfilter_batch_shader_data imgfilter_batch = {}; + if (filter == BLUR_FILTER_32) + imgfilter_batch.g_color_scale = scale * (float_t)(1.0 / 8.0); + else + imgfilter_batch.g_color_scale = scale * (float_t)(1.0 / 4.0); + imgfilter_batch.g_color_offset = offset; + imgfilter_batch.g_texture_lod = 0.0f; + rctx->imgfilter_batch_ubo.WriteMapMemory(imgfilter_batch); + + uniform_value[U_IMAGE_FILTER] = filter == BLUR_FILTER_32 ? 1 : 0; + gl_state_bind_vertex_array(rctx->box_vao); + shaders_ft.set_opengl_shader(SHADER_FT_IMGFILT); + rctx->filter_scene_ubo.Bind(0); + rctx->imgfilter_batch_ubo.Bind(1); + gl_state_active_bind_texture_2d(0, src); + glDrawArrays(GL_TRIANGLE_STRIP, (GLint)(filter * 4LL), 4); + + texture_params_restore(&tex_params[0], &tex_params[1], 0); +} diff --git a/src/CRE/render_manager.hpp b/src/CRE/render_manager.hpp new file mode 100644 index 00000000..4ec15459 --- /dev/null +++ b/src/CRE/render_manager.hpp @@ -0,0 +1,13 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#pragma once + +#include "../KKdLib/default.hpp" +#include "../KKdLib/vec.hpp" +#include +#include "render_context.hpp" + +extern void image_filter_scale(render_context* rctx, GLuint dst, GLuint src, const vec4 scale); diff --git a/src/CRE/render_texture.cpp b/src/CRE/render_texture.cpp index 38f00a2a..c7b99ae6 100644 --- a/src/CRE/render_texture.cpp +++ b/src/CRE/render_texture.cpp @@ -4,12 +4,18 @@ */ #include "render_texture.hpp" +#include "Vulkan/buffer.hpp" #include "gl_state.hpp" +#include "render_context.hpp" #include "texture.hpp" -static shader_glsl render_texture_shader; static GLuint render_texture_vao; -static GLuint render_texture_vbo; + +static VkVertexInputAttributeDescription render_texture_vertex_input_attribute_descriptions[16]; +static VkVertexInputBindingDescription render_texture_vertex_input_binding_descriptions[2]; +static Vulkan::Buffer render_texture_vertex_data; +static Vulkan::Buffer render_texture_dummy_data; + static bool render_texture_data_initialized; static uint32_t render_texture_counter; @@ -43,7 +49,7 @@ void render_texture::draw(bool depth) { if (depth && depth_texture->tex) gl_state_active_bind_texture_2d(1, depth_texture->tex); gl_state_bind_vertex_array(render_texture_vao); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); gl_state_bind_vertex_array(0); } @@ -145,130 +151,186 @@ int32_t render_texture::set_color_depth_textures(GLuint color_texture, return error; } -void render_texture::draw_custom(shader_set_data* set) { +void render_texture::draw(shader_set_data* set) { gl_state_bind_vertex_array(render_texture_vao); - set->draw_arrays(GL_TRIANGLE_STRIP, 0, 3); - gl_state_bind_vertex_array(0); + set->draw_arrays(GL_TRIANGLE_STRIP, 0, 4); } -void render_texture::draw_custom_glsl() { +void render_texture::draw_custom() { gl_state_bind_vertex_array(render_texture_vao); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 3); - gl_state_bind_vertex_array(0); + glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } -void render_texture::draw_params(shader_set_data* set, int32_t width, int32_t height, +void render_texture::draw_quad(shader_set_data* set, int32_t width, int32_t height, float_t scale, float_t param_x, float_t param_y, float_t param_z, float_t param_w) { + extern render_context* rctx_ptr; + float_t w = (float_t)max_def(width, 1); float_t h = (float_t)max_def(height, 1); - set->local_vert_set(0, scale / w, scale / h, w, h); - set->local_frag_set(0, scale / w, scale / h, w, h); - set->local_vert_set(3, param_x, param_y, param_z, param_w); - set->local_frag_set(3, param_x, param_y, param_z, param_w); + quad_shader_data quad = {}; + quad.g_texcoord_modifier = { 0.5f, 0.5f, 0.5f, 0.5f }; // x * 0.5 + 0.5 + quad.g_texel_size = { scale / w, scale / h, w, h }; + quad.g_color = { param_x, param_y, param_z, param_w }; + quad.g_texture_lod = 0.0f; + rctx_ptr->quad_ubo.WriteMapMemory(quad); + rctx_ptr->quad_ubo.Bind(0); gl_state_bind_vertex_array(render_texture_vao); - set->draw_arrays(GL_TRIANGLE_STRIP, 0, 3); - gl_state_bind_vertex_array(0); + set->draw_arrays(GL_TRIANGLE_STRIP, 0, 4); } -void render_texture::shader_set(shader_glsl* shader) { - if (shader) - shader->use(); - else - render_texture_shader.use(); -} - -void render_texture_data_init() { +void render_texture_opengl_data_init() { if (render_texture_data_initialized) return; - static const char* render_texture_vert_shader = - "#version 430 core\n" - "out VertexData {\n" - " vec2 texcoord;\n" - "} result;\n" - "\n" - "void main() {\n" - " gl_Position.x = -1.0 + float(gl_VertexID / 2) * 4.0;\n" - " gl_Position.y = 1.0 - float(gl_VertexID % 2) * 4.0;\n" - " gl_Position.z = 0.0;\n" - " gl_Position.w = 1.0;\n" - " result.texcoord = gl_Position.xy * 0.5 + 0.5;\n" - "}\n"; - - static const char* render_texture_frag_shader = - "#version 430 core\n" - "layout(location = 0) out vec4 result;\n" - "\n" - "in VertexData {\n" - " vec2 texcoord;\n" - "} frg;\n" - "\n" - "layout(binding = 0) uniform sampler2D g_color;\n" - "\n" - "void main() {\n" - " result = texture(g_color, frg.texcoord);\n" - "}\n"; - - shader_glsl_param param = {}; - param.name = "Render Texture"; - render_texture_shader.load(render_texture_vert_shader, - render_texture_frag_shader, 0, ¶m); - - const float_t verts_quad[] = { - -1.0f, 1.0f, 0.0f, 1.0f, - -1.0f, -3.0f, 0.0f, -1.0f, - 3.0f, 1.0f, 2.0f, 1.0f, - }; - glGenVertexArrays(1, &render_texture_vao); - gl_state_bind_vertex_array(render_texture_vao, true); - - glGenBuffers(1, &render_texture_vbo); - gl_state_bind_array_buffer(render_texture_vbo, true); - if (GLAD_GL_VERSION_4_4) - glBufferStorage(GL_ARRAY_BUFFER, sizeof(verts_quad), verts_quad, 0); - else - glBufferData(GL_ARRAY_BUFFER, sizeof(verts_quad), verts_quad, GL_STATIC_DRAW); - - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 16, (void*)0); // Pos - glVertexAttrib4f(3, 1.0f, 1.0f, 1.0f, 1.0f); // Color0 - glVertexAttrib4f(4, 1.0f, 1.0f, 1.0f, 1.0f); // Color1 - glEnableVertexAttribArray(8); - glVertexAttribPointer(8, 2, GL_FLOAT, GL_FALSE, 16, (void*)8); // TexCoord0 - glEnableVertexAttribArray(9); - glVertexAttribPointer(9, 2, GL_FLOAT, GL_FALSE, 16, (void*)8); // TexCoord1 - glEnableVertexAttribArray(10); - glVertexAttribPointer(10, 2, GL_FLOAT, GL_FALSE, 16, (void*)8); // TexCoord2 - glEnableVertexAttribArray(11); - glVertexAttribPointer(11, 2, GL_FLOAT, GL_FALSE, 16, (void*)8); // TexCoord3 - glEnableVertexAttribArray(12); - glVertexAttribPointer(12, 2, GL_FLOAT, GL_FALSE, 16, (void*)8); // TexCoord4 - glEnableVertexAttribArray(13); - glVertexAttribPointer(13, 2, GL_FLOAT, GL_FALSE, 16, (void*)8); // TexCoord5 - glEnableVertexAttribArray(14); - glVertexAttribPointer(14, 2, GL_FLOAT, GL_FALSE, 16, (void*)8); // TexCoord6 - glEnableVertexAttribArray(15); - glVertexAttribPointer(15, 2, GL_FLOAT, GL_FALSE, 16, (void*)8); // TexCoord7 - gl_state_bind_array_buffer(0); - gl_state_bind_vertex_array(0); render_texture_data_initialized = true; } -void render_texture_data_free() { +void render_texture_opengl_data_free() { if (!render_texture_data_initialized) return; - render_texture_shader.unload(); - - glDeleteBuffers(1, &render_texture_vbo); glDeleteVertexArrays(1, &render_texture_vao); render_texture_data_initialized = false; } +void render_texture_vulkan_data_init(VkDevice device, + VmaAllocator allocator, VkCommandPool command_pool, VkQueue queue) { + if (render_texture_data_initialized) + return; + + const float_t vertex_data[] = { + -1.0f, 1.0f, 0.0f, 1.0f, + -1.0f, -3.0f, 0.0f, -1.0f, + 3.0f, 1.0f, 2.0f, 1.0f, + }; + + const float_t dummy_data[] = { + 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, + 1.0f, 1.0f, 1.0f, 1.0f, + }; + + const size_t vertex_data_size = sizeof(vertex_data); + const size_t dummy_data_size = sizeof(dummy_data); + + VkVertexInputAttributeDescription* attribute_descriptions = render_texture_vertex_input_attribute_descriptions; + attribute_descriptions[0] = { 0, 0, VK_FORMAT_R32G32_SFLOAT, 0 }; + attribute_descriptions[1] = { 1, 1, VK_FORMAT_R32G32B32A32_SFLOAT, sizeof(float_t) * 4 }; + attribute_descriptions[2] = { 2, 1, VK_FORMAT_R32G32B32A32_SFLOAT, sizeof(float_t) * 4 }; + attribute_descriptions[3] = { 3, 1, VK_FORMAT_R32G32B32A32_SFLOAT, sizeof(float_t) * 8 }; + attribute_descriptions[4] = { 4, 1, VK_FORMAT_R32G32B32A32_SFLOAT, sizeof(float_t) * 8 }; + attribute_descriptions[5] = { 5, 1, VK_FORMAT_R32G32B32A32_SFLOAT, sizeof(float_t) * 4 }; + attribute_descriptions[6] = { 6, 1, VK_FORMAT_R32G32B32A32_SFLOAT, sizeof(float_t) * 4 }; + attribute_descriptions[7] = { 7, 1, VK_FORMAT_R32G32B32A32_SFLOAT, sizeof(float_t) * 4 }; + attribute_descriptions[8] = { 8, 0, VK_FORMAT_R32G32_SFLOAT, 0 }; + attribute_descriptions[9] = { 9, 0, VK_FORMAT_R32G32_SFLOAT, 0 }; + attribute_descriptions[10] = { 10, 1, VK_FORMAT_R32G32B32A32_SFLOAT, sizeof(float_t) * 4 }; + attribute_descriptions[11] = { 11, 1, VK_FORMAT_R32G32B32A32_SFLOAT, sizeof(float_t) * 4 }; + attribute_descriptions[12] = { 12, 1, VK_FORMAT_R32G32B32A32_SFLOAT, sizeof(float_t) * 4 }; + attribute_descriptions[13] = { 13, 1, VK_FORMAT_R32G32B32A32_SFLOAT, sizeof(float_t) * 4 }; + attribute_descriptions[14] = { 14, 1, VK_FORMAT_R32G32B32A32_SFLOAT, sizeof(float_t) * 4 }; + attribute_descriptions[15] = { 15, 1, VK_FORMAT_R32G32B32A32_SFLOAT, sizeof(float_t) * 4 }; + + VkVertexInputBindingDescription* binding_descriptions = render_texture_vertex_input_binding_descriptions; + binding_descriptions[0] = { 0, sizeof(float_t) * 4, VK_VERTEX_INPUT_RATE_VERTEX }; + binding_descriptions[1] = { 1, sizeof(float_t) * 4, VK_VERTEX_INPUT_RATE_INSTANCE }; + + render_texture_vertex_data.Create(allocator, vertex_data_size, + VK_BUFFER_USAGE_TRANSFER_DST_BIT + | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VMA_MEMORY_USAGE_AUTO, + VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT); + + render_texture_dummy_data.Create(allocator, dummy_data_size, + VK_BUFFER_USAGE_TRANSFER_DST_BIT + | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, + VMA_MEMORY_USAGE_AUTO, + VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT); + + VkCommandBufferAllocateInfo alloc_info = {}; + alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; + alloc_info.commandPool = command_pool; + alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + alloc_info.commandBufferCount = 1; + + VkCommandBuffer command_buffer; + vkAllocateCommandBuffers(device, &alloc_info, &command_buffer); + + VkCommandBufferBeginInfo begin_info = {}; + begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; + + Vulkan::Buffer staging_buffer; + staging_buffer.Create(allocator, max_def(vertex_data_size, dummy_data_size), + VK_BUFFER_USAGE_TRANSFER_SRC_BIT, + VMA_MEMORY_USAGE_AUTO, + VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | + VMA_ALLOCATION_CREATE_MAPPED_BIT); + + vkBeginCommandBuffer(command_buffer, &begin_info); + + staging_buffer.WriteMapMemory(vertex_data, 0, vertex_data_size); + + VkBufferCopy copy_region = {}; + copy_region.size = vertex_data_size; + vkCmdCopyBuffer(command_buffer, staging_buffer.data, render_texture_vertex_data.data, 1, ©_region); + + staging_buffer.WriteMapMemory(dummy_data, 0, dummy_data_size); + + copy_region = {}; + copy_region.size = dummy_data_size; + vkCmdCopyBuffer(command_buffer, staging_buffer.data, render_texture_dummy_data.data, 1, ©_region); + + vkEndCommandBuffer(command_buffer); + + staging_buffer.Destroy(); + + VkSubmitInfo submit_info = {}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &command_buffer; + + vkQueueSubmit(queue, 1, &submit_info, 0); + vkQueueWaitIdle(queue); + + vkFreeCommandBuffers(device, command_pool, 1, &command_buffer); + + render_texture_data_initialized = true; +} + +void render_texture_vulkan_data_get_vertex_input_attribute_descriptions( + const VkVertexInputAttributeDescription*& attribute_descriptions, uint32_t& attribute_description_count) { + attribute_descriptions = render_texture_vertex_input_attribute_descriptions; + attribute_description_count = sizeof(render_texture_vertex_input_attribute_descriptions) + / sizeof(VkVertexInputAttributeDescription); +} + +void render_texture_vulkan_data_get_vertex_input_binding_descriptions( + const VkVertexInputBindingDescription*& binding_descriptions, uint32_t& binding_description_count) { + binding_descriptions = render_texture_vertex_input_binding_descriptions; + binding_description_count = sizeof(render_texture_vertex_input_binding_descriptions) + / sizeof(VkVertexInputBindingDescription); +} + +void render_texture_vulkan_data_free(VkDevice device, VmaAllocator allocator) { + if (!render_texture_data_initialized) + return; + + render_texture_dummy_data.Destroy(); + render_texture_vertex_data.Destroy(); + + memset(render_texture_vertex_input_binding_descriptions, + 0, sizeof(render_texture_vertex_input_binding_descriptions)); + memset(render_texture_vertex_input_attribute_descriptions, + 0, sizeof(render_texture_vertex_input_attribute_descriptions)); + + render_texture_data_initialized = false; +} + static int32_t render_texture_framebuffer_init(render_texture* rt, int32_t max_level) { glGenFramebuffers(max_level + 1, rt->fbos); return -(glGetError() != GL_ZERO); diff --git a/src/CRE/render_texture.hpp b/src/CRE/render_texture.hpp index 0999df32..91de441f 100644 --- a/src/CRE/render_texture.hpp +++ b/src/CRE/render_texture.hpp @@ -7,7 +7,6 @@ #include "shared.hpp" #include "shader.hpp" -#include "shader_glsl.hpp" #include "texture.hpp" struct render_texture { @@ -30,12 +29,19 @@ struct render_texture { int32_t set_color_depth_textures(GLuint color_texture, int32_t max_level, GLuint depth_texture, bool stencil = false); - static void draw_custom(shader_set_data* set); - static void draw_custom_glsl(); - static void draw_params(shader_set_data* set, int32_t width, int32_t height, float_t scale = 1.0f, + static void draw(shader_set_data* set); + static void draw_custom(); + static void draw_quad(shader_set_data* set, int32_t width, int32_t height, float_t scale = 1.0f, float_t param_x = 1.0f, float_t param_y = 1.0f, float_t param_z = 1.0f, float_t param_w = 1.0f); - static void shader_set(shader_glsl* shader); }; -extern void render_texture_data_init(); -extern void render_texture_data_free(); +extern void render_texture_opengl_data_init(); +extern void render_texture_opengl_data_free(); + +extern void render_texture_vulkan_data_init(VkDevice device, + VmaAllocator allocator, VkCommandPool command_pool, VkQueue queue); +extern void render_texture_vulkan_data_get_vertex_input_attribute_descriptions( + const VkVertexInputAttributeDescription*& attribute_descriptions, uint32_t& attribute_description_count); +extern void render_texture_vulkan_data_get_vertex_input_binding_descriptions( + const VkVertexInputBindingDescription*& binding_descriptions, uint32_t& binding_description_count); +extern void render_texture_vulkan_data_free(VkDevice device, VmaAllocator allocator); diff --git a/src/CRE/rob/ex_block.cpp b/src/CRE/rob/ex_block.cpp index 59b3d768..69e03af5 100644 --- a/src/CRE/rob/ex_block.cpp +++ b/src/CRE/rob/ex_block.cpp @@ -6,18 +6,7 @@ #include "rob.hpp" #include "../../KKdLib/key_val.hpp" #include "../../KKdLib/str_utils.hpp" -#include "../draw_task.hpp" - -struct osage_setting { - std::map, osage_setting_osg_cat> cat; - std::map obj; - - osage_setting(); - ~osage_setting(); - - void parse(key_val* kv); - rob_osage_parts parse_parts_string(std::string& s); -}; +#include "skin_param.hpp" struct exp_func_op1 { const char* name; @@ -76,9 +65,6 @@ static float_t exp_tan(float_t v1); static void closest_pt_segment_segment(vec3& vec, const vec3& p0, const vec3& p1, const OsageCollision::Work* cls); -static const osage_setting_osg_cat* osage_setting_data_get_cat_value( - object_info* obj_info, const char* root_node); - static void sub_140218560(RobCloth* rob_cls, float_t step, bool a3); static void sub_1402187D0(RobCloth* rob_cls, bool a2); static void sub_1402196D0(RobCloth* rob_cls); @@ -93,7 +79,7 @@ static void sub_14021D480(RobCloth* rob_cls); static void sub_14021DC60(RobCloth* rob_cls, float_t step); static void sub_14021D840(RobCloth* rob_cls); static void sub_14047C800(RobOsage* rob_osg, mat4* mat, vec3* parent_scale, - float_t step, bool disable_wind, bool ring_coli, bool has_children_node); + float_t step, bool disable_external_force, bool ring_coli, bool has_children_node); static void sub_14047E1C0(RobOsage* rob_osg, vec3* scale); static void sub_14047EE90(RobOsage* rob_osg, mat4* mat); static void sub_14047F110(RobOsage* rob_osg, mat4* mat, vec3* parent_scale, bool init_rot); @@ -104,11 +90,12 @@ static void sub_140482490(RobOsageNode* node, float_t step, float_t a3); static void sub_140482F30(vec3* pos1, vec3* pos2, float_t length); static void sub_14047D8C0(RobOsage* rob_osg, mat4* mat, vec3* parent_scale, float_t step, bool a5); static void sub_14047C750(RobOsage* rob_osg, mat4* mat, vec3* parent_scale, float_t step); -static void sub_14047C770(RobOsage* rob_osg, mat4* mat, vec3* parent_scale, float_t step, bool a5); +static void sub_14047C770(RobOsage* rob_osg, mat4* mat, + vec3* parent_scale, float_t step, bool disable_external_force); static void sub_14047D620(RobOsage* rob_osg, float_t step); static void sub_14047ECA0(RobOsage* rob_osg, float_t step); static void sub_14047F990(RobOsage* rob_osg, mat4* a2, vec3* parent_scale, bool a4); -static void sub_140480260(RobOsage* rob_osg, mat4* mat, vec3* parent_scale, float_t step, bool v5); +static void sub_140480260(RobOsage* rob_osg, mat4* mat, vec3* parent_scale, float_t step, bool disable_external_force); static void sub_140482100(struc_476* a1, opd_blend_data* a2, struc_477* a3); static void sub_140482DF0(struc_477* dst, struc_477* src0, struc_477* src1, float_t blend); static bool sub_140482FF0(mat4& mat, vec3& trans, skin_param_hinge* hinge, vec3* rot, int32_t& yz_order); @@ -163,7 +150,6 @@ static const exp_func_op3 exp_func_op3_array[] = { { 0 , 0 }, }; -osage_setting osage_setting_data; size_t qword_140FBDF88 = 0; int32_t rob_cloth_update_vertices_flags = 0x03; bool rob_cloth_update_normals_select = false; @@ -222,7 +208,7 @@ void ExNullBlock::Field_10() { field_59 = false; } -void ExNullBlock::Field_18(int32_t stage, bool a3) { +void ExNullBlock::Field_18(int32_t stage, bool disable_external_force) { } @@ -241,7 +227,7 @@ void ExNullBlock::SetOsagePlayData() { Field_20(); } -void ExNullBlock::Disp(mat4* mat, render_context* rctx) { +void ExNullBlock::Disp(const mat4* mat, render_context* rctx) { } @@ -353,6 +339,20 @@ void RobOsageNodeData::Reset() { skp_osg_node.hinge.limit(); } +void RobOsageNodeData::SetForce(skin_param_osage_root& skp_root, + skin_param_osage_node* skp_osg_node, size_t index) { + this->skp_osg_node = *skp_osg_node; + this->skp_osg_node.hinge.limit(); + + float_t force = skp_root.force; + float_t force_gain = skp_root.force_gain; + for (; index >= 4; index -= 4) + force = force * force_gain * force_gain * force_gain * force_gain; + for (; index; index--) + force *= force_gain; + this->force = force; +} + struc_477::struc_477() : length() { } @@ -401,55 +401,6 @@ void RobOsageNode::Reset() { mat = mat4_null; } -SkinParam::CollisionParam::CollisionParam() : type(), node_idx(), pos() { - radius = 0.2f; -} - -SkinParam::CollisionParam::~CollisionParam() { - -} - -skin_param_osage_root_normal_ref::skin_param_osage_root_normal_ref() { - -} - -skin_param_osage_root_normal_ref::~skin_param_osage_root_normal_ref() { - -} - -skin_param_osage_root_boc::skin_param_osage_root_boc() : ed_node(), st_node() { - -} - -skin_param_osage_root_boc::~skin_param_osage_root_boc() { - -} - -skin_param_osage_root::skin_param_osage_root() : field_0(), force(), force_gain(), -rot_y(), rot_z(), init_rot_y(), init_rot_z(), coli_r(), yz_order(), coli_type(), stiffness() { - air_res = 0.5f; - hinge_y = 90.0f; - hinge_z = 90.0f; - name ="NO-PARAM"; - friction = 1.0f; - wind_afc = 0.5f; - move_cancel = -0.01f; - coli.resize(13); -} - -skin_param_osage_root::~skin_param_osage_root() { - -} - -skin_param::skin_param() : friction(), wind_afc(), air_res(), rot(), init_rot(), -coli_type(), stiffness(), move_cancel(), coli_r(), force(), force_gain(), colli_tgt_osg() { - reset(); -} - -skin_param::~skin_param() { - -} - OsageCollision::Work::Work() : type(), radius(), pos(), vec_center(), vec_center_length(), vec_center_length_squared(), friction() { @@ -458,16 +409,8 @@ vec_center(), vec_center_length(), vec_center_length_squared(), friction() { OsageCollision::Work::~Work() { } -// 0x140484FE0 -int32_t OsageCollision::Work::osage_capsule_cls(vec3& p0, vec3& p1, const float_t& cls_r) { - return OsageCollision::osage_capsule_cls(p0, p1, cls_r, this); -} - -// 0x140485180 -int32_t OsageCollision::Work::osage_cls(vec3& p, const float_t& cls_r) { - return OsageCollision::osage_cls(p, cls_r, this, 0); -} +// 0x140485450 void OsageCollision::Work::update_cls_work(OsageCollision::Work* cls, SkinParam::CollisionParam* cls_param, mat4* transform) { cls->type = SkinParam::CollisionTypeEnd; @@ -481,7 +424,7 @@ void OsageCollision::Work::update_cls_work(OsageCollision::Work* cls, &cls_param->pos[0], &cls->pos[0]); switch (cls->type) { - case SkinParam::CollisionTypeBall: + case SkinParam::CollisionTypePlane: mat4_mult_vec3(&transform[cls_param->node_idx[1]], &cls_param->pos[1], &cls->pos[1]); break; @@ -500,6 +443,7 @@ void OsageCollision::Work::update_cls_work(OsageCollision::Work* cls, cls->type = SkinParam::CollisionTypeEnd; } +// 0x140485410 void OsageCollision::Work::update_cls_work(OsageCollision::Work* cls, std::vector& cls_list, mat4* mats) { if (cls_list.size()) { @@ -729,6 +673,11 @@ void OsageCollision::get_nearest_line2point(vec3& nearest, const vec3& p0, const nearest = p1; } +// 0x140484FE0 +int32_t OsageCollision::osage_capsule_cls(const OsageCollision::Work* cls, vec3& p0, vec3& p1, const float_t& cls_r) { + return OsageCollision::osage_capsule_cls(p0, p1, cls_r, cls); +} + // 0x140485000 int32_t OsageCollision::osage_capsule_cls(vec3& p0, vec3& p1, const float_t& cls_r, const OsageCollision::Work* cls) { if (!cls) @@ -759,6 +708,11 @@ int32_t OsageCollision::osage_capsule_cls(vec3& p0, vec3& p1, const float_t& cls return v8; } +// 0x140485180 +int32_t OsageCollision::osage_cls(const OsageCollision::Work* cls, vec3& p, const float_t& cls_r) { + return OsageCollision::osage_cls(p, cls_r, cls, 0); +} + // 0x140485220 int32_t OsageCollision::osage_cls(vec3& p, const float_t& cls_r, const OsageCollision::Work* cls, float_t* fric) { if (!cls) @@ -786,10 +740,9 @@ int32_t OsageCollision::osage_cls(vec3& p, const float_t& cls_r, const OsageColl break; } - if (fric && v11 > 0) { - if (*fric > cls->friction) - *fric = cls->friction; - } + if (fric && v11 > 0 && *fric > cls->friction) + *fric = cls->friction; + cls++; v8 += v11; p += vec; @@ -805,7 +758,7 @@ int32_t OsageCollision::osage_cls_work_list(vec3& p, const float_t& cls_r, const } osage_ring_data::osage_ring_data() : ring_rectangle_x(), ring_rectangle_y(), -ring_rectangle_width(), ring_rectangle_height(), field_18() { +ring_rectangle_width(), ring_rectangle_height(), init() { ring_height = -1000.0f; ring_out_height = -1000.0f; } @@ -971,12 +924,12 @@ void RobCloth::ColiSet(mat4* mats) { OsageCollision::Work::update_cls_work(coli_ring, ring.skp_root_coli, mats); } -void RobCloth::Disp(mat4* mat, render_context* rctx) { +void RobCloth::Disp(const mat4* mat, render_context* rctx) { obj* obj = object_storage_get_obj(itm_eq_obj->obj_info); if (!obj) return; - std::vector* tex = object_storage_get_obj_set_textures(itm_eq_obj->obj_info.set_id); + std::vector* tex = object_storage_get_obj_set_textures(itm_eq_obj->obj_info.set_id); vec3 center; vec3_add(nodes.data()[0].trans, nodes.data()[root_count * nodes_count - 1].trans, center); @@ -997,9 +950,9 @@ void RobCloth::Disp(mat4* mat, render_context* rctx) { } } - rctx->object_data.set_texture_pattern((int32_t)itm_eq_obj->texture_pattern.size(), itm_eq_obj->texture_pattern.data()); - draw_task_add_draw_object_by_obj(rctx, mat, &o, tex, vertex_buffer, index_buffer, 0, itm_eq_obj->alpha); - rctx->object_data.set_texture_pattern(); + rctx->disp_manager.set_texture_pattern((int32_t)itm_eq_obj->texture_pattern.size(), itm_eq_obj->texture_pattern.data()); + rctx->disp_manager.entry_obj_by_obj(mat, &o, tex, vertex_buffer, index_buffer, 0, itm_eq_obj->alpha); + rctx->disp_manager.set_texture_pattern(); } void RobCloth::InitData(size_t root_count, size_t nodes_count, obj_skin_block_cloth_root* root, @@ -1514,7 +1467,7 @@ void ExClothBlock::Field_10() { field_59 = false; } -void ExClothBlock::Field_18(int32_t stage, bool a3) { +void ExClothBlock::Field_18(int32_t stage, bool disable_external_force) { } @@ -1533,7 +1486,7 @@ void ExClothBlock::SetOsagePlayData() { rob.SetOsagePlayData(item_equip_object->item_equip->opd_blend_data); } -void ExClothBlock::Disp(mat4* mat, render_context* rctx) { +void ExClothBlock::Disp(const mat4* mat, render_context* rctx) { rob.UpdateDisp(); rob.Disp(mat, rctx); } @@ -1593,7 +1546,7 @@ void ExClothBlock::SetOsageReset() { rob.osage_reset = true; } -void ExClothBlock::SetSkinParam(struc_571* skp) { +void ExClothBlock::SetSkinParam(skin_param_file_data* skp) { rob.skin_param_ptr = &skp->skin_param; } @@ -1610,18 +1563,6 @@ void ExClothBlock::SetSkinParamOsageRoot(skin_param_osage_root* skp_root) { rob.SetWindDirection(wind_direction); } -struc_571::struc_571() : field_88() { - -} - -struc_571::~struc_571() { - -} - -osage_setting_osg_cat::osage_setting_osg_cat() : exf() { - parts = ROB_OSAGE_PARTS_NONE; -} - RobOsage::RobOsage() : skin_param_ptr(), osage_setting(), field_2A0(), field_2A1(), field_2A4(), wind_direction(), field_1EB4(), yz_order(), field_1EBC(), parent_mat_ptr(), parent_mat(), move_cancel(), field_1F0C(), osage_reset(), field_1F0E(), field_1F0F(), set_external_force(), external_force(), reset_data_list() { @@ -2098,7 +2039,7 @@ void ExOsageBlock::Field_10() { field_59 = false; } -void ExOsageBlock::Field_18(int32_t stage, bool a3) { +void ExOsageBlock::Field_18(int32_t stage, bool disable_external_force) { rob_chara_item_equip* rob_itm_equip = item_equip_object->item_equip; float_t step = get_delta_frame() * rob_itm_equip->step; if (rob_itm_equip->opd_blend_data.size() && rob_itm_equip->opd_blend_data[0].field_C) @@ -2114,7 +2055,8 @@ void ExOsageBlock::Field_18(int32_t stage, bool a3) { case 2: if ((stage == 1 && field_58) || (stage == 2 && rob.field_2A0)) { SetWindDirection(); - sub_14047C800(&rob, parent_node_mat, &parent_scale, step, a3, true, has_children_node); + sub_14047C800(&rob, parent_node_mat, + &parent_scale, step, disable_external_force, true, has_children_node); } break; case 3: @@ -2126,7 +2068,7 @@ void ExOsageBlock::Field_18(int32_t stage, bool a3) { break; case 5: { sub_14047D8C0(&rob, parent_node_mat, &parent_scale, step, false); - sub_140480260(&rob, parent_node_mat, &parent_scale, step, a3); + sub_140480260(&rob, parent_node_mat, &parent_scale, step, disable_external_force); field_59 = true; } break; } @@ -2162,7 +2104,7 @@ void ExOsageBlock::SetOsagePlayData() { parent_bone_node->exp_data.parent_scale, item_equip_object->item_equip->opd_blend_data); } -void ExOsageBlock::Disp(mat4* mat, render_context* rctx) { +void ExOsageBlock::Disp(const mat4* mat, render_context* rctx) { } @@ -2184,7 +2126,7 @@ void ExOsageBlock::Field_48() { SetWindDirection(); rob.ColiSet(mats); vec3 parent_scale = parent_bone_node->exp_data.parent_scale; - sub_14047F990(&rob, parent_bone_node->ex_data_mat, &parent_scale, 0); + sub_14047F990(&rob, parent_bone_node->ex_data_mat, &parent_scale, false); field_1FF8 &= ~2; } @@ -2198,7 +2140,7 @@ void ExOsageBlock::Field_50() { vec3 parent_scale = parent_bone_node->exp_data.parent_scale; rob.ColiSet(mats); - sub_14047C770(&rob, parent_bone_node->ex_data_mat, &parent_scale, step, 1); + sub_14047C770(&rob, parent_bone_node->ex_data_mat, &parent_scale, step, true); float_t step = 0.5f * this->step; this->step = max_def(step, 1.0f); } @@ -2239,7 +2181,7 @@ void ExOsageBlock::SetOsageReset() { rob.osage_reset = true; } -void ExOsageBlock::SetSkinParam(struc_571* skp) { +void ExOsageBlock::SetSkinParam(skin_param_file_data* skp) { if (skp->nodes_data.size() == rob.nodes.size() - 1) { size_t node_index = 0; RobOsageNode* i_begin = rob.nodes.data() + 1; @@ -2308,7 +2250,7 @@ void ExConstraintBlock::Field_10() { field_59 = false; } -void ExConstraintBlock::Field_18(int32_t stage, bool a3) { +void ExConstraintBlock::Field_18(int32_t stage, bool disable_external_force) { if (field_59) return; @@ -2342,7 +2284,7 @@ void ExConstraintBlock::SetOsagePlayData() { Field_20(); } -void ExConstraintBlock::Disp(mat4* mat, render_context* rctx) { +void ExConstraintBlock::Disp(const mat4* mat, render_context* rctx) { } @@ -2483,7 +2425,7 @@ void ExConstraintBlock::DataSet() { mat4 mat; mat4_inverse_normalized(parent_bone_node->ex_data_mat, &mat); mat4_mult(bone_node_ptr->mat, &mat, &mat); - mat4_get_rotation(&mat, &bone_node_ptr->exp_data.rotation); + mat4_get_rotation(&mat, &exp_data->rotation); mat4_get_translation(&mat, &exp_data->position); if (fabsf(parent_scale.x) > 0.000001f) exp_data->position.x /= parent_scale.x; @@ -2493,7 +2435,7 @@ void ExConstraintBlock::DataSet() { exp_data->position.z /= parent_scale.z; *bone_node_ptr->ex_data_mat = *bone_node_ptr->mat; mat4_scale_rot(bone_node_ptr->mat, &parent_scale, bone_node_ptr->mat); - vec3_mult(exp_data->scale, parent_scale, exp_data->parent_scale); + exp_data->parent_scale = exp_data->scale * parent_scale; } void ExConstraintBlock::InitData(rob_chara_item_equip_object* itm_eq_obj, @@ -2600,7 +2542,7 @@ void ExExpressionBlock::Field_10() { field_59 = false; } -void ExExpressionBlock::Field_18(int32_t stage, bool a3) { +void ExExpressionBlock::Field_18(int32_t stage, bool disable_external_force) { if (field_59) return; @@ -2634,7 +2576,7 @@ void ExExpressionBlock::SetOsagePlayData() { Field_20(); } -void ExExpressionBlock::Disp(mat4* mat, render_context* rctx) { +void ExExpressionBlock::Disp(const mat4* mat, render_context* rctx) { } @@ -2785,374 +2727,6 @@ void ExExpressionBlock::InitData(rob_chara_item_equip_object* itm_eq_obj, step = !((ssize_t)index - ITEM_TE_R <= 1); } -void osage_setting_data_init() { - osage_setting_data.cat = {}; - osage_setting_data.obj = {}; -} - -bool osage_setting_data_load_file(void* data, const char* path, const char* file, uint32_t hash) { - key_val kv; - if (key_val::load_file(&kv, path, file, hash)) { - osage_setting_data.parse(&kv); - return true; - } - return false; -} - -bool osage_setting_data_obj_has_key(object_info key) { - return osage_setting_data.obj.find(key) != osage_setting_data.obj.end(); -} - -void osage_setting_data_free() { - osage_setting_data.cat.clear(); - osage_setting_data.obj.clear(); -} - -void skin_param_osage_node_parse(void* kv, const char* name, - std::vector* vec, skin_param_osage_root& skp_root) { - key_val* _kv = (key_val*)kv; - if (!_kv->open_scope(name)) - return; - - if (!_kv->open_scope("node")) { - _kv->close_scope(); - return; - } - - int32_t count; - if (_kv->read("length", count)) { - size_t c = vec->size(); - if (c < count) - vec->resize(count); - } - else - vec->clear(); - - int32_t j = 0; - for (auto i = vec->begin(); i != vec->end(); i++, j++) { - if (!_kv->open_scope_fmt(j)) - continue; - - *i = skin_param_osage_node(); - - float_t coli_r = 0.0f; - if (_kv->read("coli_r", coli_r)) - i->coli_r = coli_r; - - float_t weight = 0.0f; - if (_kv->read("weight", weight)) - i->weight = weight; - - float_t inertial_cancel = 0.0f; - if (_kv->read("inertial_cancel", inertial_cancel)) - i->inertial_cancel = inertial_cancel; - - i->hinge.ymin = -skp_root.hinge_y; - i->hinge.ymax = skp_root.hinge_y; - i->hinge.zmin = -skp_root.hinge_z; - i->hinge.zmax = skp_root.hinge_z; - - float_t hinge_ymax = 0.0f; - if (_kv->read("hinge_ymax", hinge_ymax)) - i->hinge.ymax = hinge_ymax; - - float_t hinge_ymin = 0.0f; - if (_kv->read("hinge_ymin", hinge_ymin)) - i->hinge.ymin = hinge_ymin; - - float_t hinge_zmax = 0.0f; - if (_kv->read("hinge_zmax", hinge_zmax)) - i->hinge.zmax = hinge_zmax; - - float_t hinge_zmin = 0.0f; - if (_kv->read("hinge_zmin", hinge_zmin)) - i->hinge.zmin = hinge_zmin; - _kv->close_scope(); - } - - _kv->close_scope(); - _kv->close_scope(); -} - -void skin_param_osage_root_parse(void* kv, const char* name, - skin_param_osage_root& skp_root, bone_database* bone_data) { - key_val* _kv = (key_val*)kv; - if (!_kv->open_scope(name)) - return; - - int32_t node_length = 0; - _kv->read("node.length", node_length); - - if (!_kv->open_scope("root")) { - _kv->close_scope(); - return; - } - - float_t force = 0.0f; - float_t force_gain = 0.0f; - if (_kv->read("force", force) - && _kv->read("force_gain", force_gain)) { - skp_root.force = force; - skp_root.force_gain = force_gain; - } - - float_t air_res = 0.0f; - if (_kv->read("air_res", air_res)) - skp_root.air_res = min_def(air_res, 0.9f); - - float_t rot_y = 0.0f; - float_t rot_z = 0.0f; - if (_kv->read("rot_y", rot_y) - && _kv->read("rot_z", rot_z)) { - skp_root.rot_y = rot_y; - skp_root.rot_z = rot_z; - } - - float_t friction = 0.0f; - if (_kv->read("friction", friction)) - skp_root.friction = friction; - - float_t wind_afc = 0; - if (_kv->read("wind_afc", wind_afc)) { - skp_root.wind_afc = wind_afc; - if (!str_utils_compare_length(name, utf8_length(name), "c_opa", 5)) - skp_root.wind_afc = 0.0f; - } - - int32_t coli_type = 0; - if (_kv->read("coli_type", coli_type)) - skp_root.coli_type = coli_type; - - float_t init_rot_y = 0.0f; - float_t init_rot_z = 0.0f; - if (_kv->read("init_rot_y", init_rot_y) - && _kv->read("init_rot_z", init_rot_z)) { - skp_root.init_rot_y = init_rot_y; - skp_root.init_rot_z = init_rot_z; - } - - float_t hinge_y = 0.0f; - float_t hinge_z = 0.0f; - if (_kv->read("hinge_y", hinge_y) - && _kv->read("hinge_z", hinge_z)) { - skp_root.hinge_y = hinge_y; - skp_root.hinge_z = hinge_z; - } - - float_t coli_r = 0.0f; - if (_kv->read("coli_r", coli_r)) - skp_root.coli_r = coli_r; - - float_t stiffness = 0.0f; - if (_kv->read("stiffness", stiffness)) - skp_root.stiffness = stiffness; - - float_t move_cancel = 0.0f; - if (_kv->read("move_cancel", move_cancel)) - skp_root.move_cancel = move_cancel; - - int32_t count; - if (_kv->read("coli", "length", count)) { - std::vector& vc = skp_root.coli; - for (int32_t i = 0; i < count; i++) { - if (!_kv->open_scope_fmt(i)) - continue; - - SkinParam::CollisionParam* c = &vc[i]; - - int32_t type = 0; - if (!_kv->read("type", type)) { - _kv->close_scope(); - break; - } - c->type = (SkinParam::CollisionType)type; - - float_t radius = 0; - if (!_kv->read("radius", radius)) { - _kv->close_scope(); - break; - } - c->radius = radius; - - const char* bone0_name; - if (!_kv->read("bone.0.name", bone0_name)) { - _kv->close_scope(); - break; - } - - c->node_idx[0] = bone_data->get_skeleton_object_bone_index( - bone_database_skeleton_type_to_string(BONE_DATABASE_SKELETON_COMMON), bone0_name); - - vec3 bone0_pos = 0.0f; - if (!_kv->read("bone.0.posx", bone0_pos.x) - || !_kv->read("bone.0.posy", bone0_pos.y) - || !_kv->read("bone.0.posz", bone0_pos.z)) { - _kv->close_scope(); - break; - } - - c->pos[0] = bone0_pos; - - const char* bone1_name; - if (_kv->read("bone.1.name", bone1_name)) { - c->node_idx[1] = bone_data->get_skeleton_object_bone_index( - bone_database_skeleton_type_to_string(BONE_DATABASE_SKELETON_COMMON), bone1_name); - - vec3 bone1_pos = 0.0f; - if (!_kv->read("bone.1.posx", bone1_pos.x) - || !_kv->read("bone.1.posy", bone1_pos.y) - || !_kv->read("bone.1.posz", bone1_pos.z)) { - _kv->close_scope(); - break; - } - - c->pos[1] = bone1_pos; - } - _kv->close_scope(); - } - _kv->close_scope(); - } - - if (_kv->read("boc", "length", count)) { - std::vector* vb = &skp_root.boc; - - vb->resize(count); - for (int32_t i = 0; i < count; i++) { - if (!_kv->open_scope_fmt(i)) - continue; - - int32_t st_node = 0; - std::string ed_root; - int32_t ed_node = 0; - if (_kv->read("st_node", st_node) - && st_node < node_length - && _kv->read("ed_root", ed_root) - && _kv->read("ed_node", ed_node) - && ed_node < node_length) { - skin_param_osage_root_boc b; - b.st_node = st_node; - b.ed_root.assign(ed_root); - b.ed_node = ed_node; - vb->push_back(b); - } - _kv->close_scope(); - } - _kv->close_scope(); - } - - std::string colli_tgt_osg; - if (_kv->read("colli_tgt_osg", colli_tgt_osg)) - skp_root.colli_tgt_osg.assign(colli_tgt_osg); - - if (_kv->read("normal_ref", "length", count)) { - std::vector* vnr = &skp_root.normal_ref; - - vnr->resize(count); - for (int32_t i = 0; i < count; i++) { - if (!_kv->open_scope_fmt(i)) - continue; - - std::string n; - if (_kv->read("N", n)) { - skin_param_osage_root_normal_ref nr; - nr.n.assign(n); - _kv->read("U", nr.u); - _kv->read("D", nr.d); - _kv->read("L", nr.l); - _kv->read("R", nr.r); - vnr->push_back(nr); - } - _kv->close_scope(); - } - _kv->close_scope(); - } - - _kv->close_scope(); - _kv->close_scope(); -} - -osage_setting::osage_setting() { - -} - -osage_setting::~osage_setting() { - -} - -void osage_setting::parse(key_val* kv) { - int32_t count; - if (kv->read("cat", "length", count)) { - for (int32_t i = 0; i < count; i++) { - if (!kv->open_scope_fmt(i)) - continue; - - std::string name; - kv->read("name", name); - - int32_t count; - if (kv->read("osg", "length", count)) { - for (int32_t j = 0; j < count; j++) { - if (!kv->open_scope_fmt(j)) - continue; - - int32_t exf = 0; - kv->read("exf", exf); - - std::string parts; - kv->read("parts", parts); - - std::string root; - kv->read("root", root); - - std::pair key = { name, root }; - auto elem = cat.find(key); - if (elem == cat.end()) - elem = cat.insert({ key, {} }).first; - - elem->second.exf = exf; - elem->second.parts = parse_parts_string(parts); - kv->close_scope(); - } - kv->close_scope(); - } - kv->close_scope(); - } - kv->close_scope(); - } - - data_struct* aft_data = &data_list[DATA_AFT]; - object_database* aft_obj_db = &aft_data->data_ft.obj_db; - - if (kv->read("obj", "length", count)) { - for (int32_t i = 0; i < count; i++) { - if (!kv->open_scope_fmt(i)) - continue; - - std::string cat; - kv->read("cat", cat); - - std::string name; - kv->read("name", name); - - obj.insert_or_assign(aft_obj_db->get_object_info(name.c_str()), cat); - kv->close_scope(); - } - kv->close_scope(); - } -} - -rob_osage_parts osage_setting::parse_parts_string(std::string& s) { - static const char* parts_string_array[] = { - "LEFT", "RIGHT", "CENTER", "LONG_C", "SHORT_L", "SHORT_R", - "APPEND_L", "APPEND_R", "MUFFLER", "WHITE_ONE_L", "PONY", "ANGEL_L", "ANGEL_R", - }; - - for (const char*& i : parts_string_array) - if (!s.compare(i)) - return (rob_osage_parts)(&i - parts_string_array); - return ROB_OSAGE_PARTS_NONE; -} - static float_t ex_expression_block_stack_get_value(ex_expression_block_stack* stack) { float_t value = 0.0f; switch (stack->type) { @@ -3408,20 +2982,6 @@ static void closest_pt_segment_segment(vec3& vec, const vec3& p0, const vec3& p1 } } -static const osage_setting_osg_cat* osage_setting_data_get_cat_value( - object_info* obj_info, const char* root_node) { - auto elem_obj = osage_setting_data.obj.find(*obj_info); - if (elem_obj == osage_setting_data.obj.end()) - return 0; - - std::pair key = { elem_obj->second, root_node }; - auto elem_cat = osage_setting_data.cat.find(key); - if (elem_cat == osage_setting_data.cat.end()) - return 0; - - return &elem_cat->second; -} - static void sub_140218560(RobCloth* rob_cls, float_t step, bool a3) { sub_140219940(rob_cls); if (rob_cls->osage_reset) { @@ -3774,8 +3334,8 @@ void sub_14021AA60(RobCloth* rob_cls, float_t step, bool a3) { int32_t v39 = OsageCollision::osage_cls_work_list(node->trans, rob_cls->skin_param_ptr->coli_r, rob_cls->ring.coli, &fric); - v39 += rob_cls->coli_ring->osage_cls(node->trans, rob_cls->skin_param_ptr->coli_r); - v39 += rob_cls->coli->osage_cls(node->trans, rob_cls->skin_param_ptr->coli_r); + v39 += OsageCollision::osage_cls(rob_cls->coli_ring, node->trans, rob_cls->skin_param_ptr->coli_r); + v39 += OsageCollision::osage_cls(rob_cls->coli, node->trans, rob_cls->skin_param_ptr->coli_r); if (v19 > node->trans.y && v19 < 1001.0) { node->trans.y = v19; @@ -3848,8 +3408,8 @@ static void sub_14021D480(RobCloth* rob_cls) { OsageCollision::osage_cls_work_list(node->trans, rob_cls->skin_param_ptr->coli_r, rob_cls->ring.coli); - rob_cls->coli_ring->osage_cls(node->trans, rob_cls->skin_param_ptr->coli_r); - rob_cls->coli->osage_cls(node->trans, rob_cls->skin_param_ptr->coli_r); + OsageCollision::osage_cls(rob_cls->coli_ring, node->trans, rob_cls->skin_param_ptr->coli_r); + OsageCollision::osage_cls(rob_cls->coli, node->trans, rob_cls->skin_param_ptr->coli_r); if (v10 > node->trans.y && v10 < 1001.0f) node->trans.y = v10; @@ -3883,7 +3443,7 @@ static void sub_14021D840(RobCloth* rob_cls) { } static void sub_14047C800(RobOsage* rob_osg, mat4* mat, vec3* parent_scale, - float_t step, bool disable_wind, bool ring_coli, bool has_children_node) { + float_t step, bool disable_external_force, bool ring_coli, bool has_children_node) { if (!rob_osg->nodes.size()) return; @@ -3943,7 +3503,7 @@ static void sub_14047C800(RobOsage* rob_osg, mat4* mat, vec3* parent_scale, vec3_mult_scalar(v26->external_force, weight, v111); vec3_add(v126, v111, v126); - if (!disable_wind) { + if (!disable_external_force) { vec3_mult_scalar(rob_osg->wind_direction, rob_osg->skin_param_ptr->wind_afc, v111); vec3_add(v126, v111, v126); } @@ -4308,8 +3868,8 @@ static void sub_14047D8C0(RobOsage* rob_osg, mat4* mat, vec3* parent_scale, floa v35->field_C8 = (float_t)OsageCollision::osage_cls_work_list(v35->trans, skp_osg_node->coli_r, vec_ring_coli, &v35->field_CC); if (!rob_osg->field_1F0F) { - v35->field_C8 += (float_t)coli_ring->osage_cls(v35->trans, skp_osg_node->coli_r); - v35->field_C8 += (float_t)coli->osage_cls(v35->trans, skp_osg_node->coli_r); + v35->field_C8 += (float_t)OsageCollision::osage_cls(coli_ring, v35->trans, skp_osg_node->coli_r); + v35->field_C8 += (float_t)OsageCollision::osage_cls(coli, v35->trans, skp_osg_node->coli_r); } sub_140482180(&v35[0], v60); } @@ -4377,10 +3937,20 @@ static void sub_14047C750(RobOsage* rob_osg, mat4* mat, vec3* parent_scale, floa sub_14047C770(rob_osg, mat, parent_scale, step, false); } -static void sub_14047C770(RobOsage* rob_osg, mat4* mat, vec3* parent_scale, float_t step, bool a5) { - sub_14047C800(rob_osg, mat, parent_scale, step, a5, false, false); - sub_14047D8C0(rob_osg, mat, parent_scale, step, true); - sub_140480260(rob_osg, mat, parent_scale, step, a5); +bool toggle1[3] = { + true, + true, + true, +}; + +static void sub_14047C770(RobOsage* rob_osg, mat4* mat, + vec3* parent_scale, float_t step, bool disable_external_force) { + if (toggle1[0]) + sub_14047C800(rob_osg, mat, parent_scale, step, disable_external_force, false, false); + if (toggle1[1]) + sub_14047D8C0(rob_osg, mat, parent_scale, step, true); + if (toggle1[2]) + sub_140480260(rob_osg, mat, parent_scale, step, disable_external_force); } static void sub_14047D620(RobOsage* rob_osg, float_t step) { @@ -4398,16 +3968,16 @@ static void sub_14047D620(RobOsage* rob_osg, float_t step) { RobOsageNodeData* data = i->data_ptr; for (RobOsageNode*& j : data->boc) { float_t v17 = (float_t)( - coli->osage_capsule_cls(i->trans, j->trans, data->skp_osg_node.coli_r) - + coli_ring->osage_capsule_cls(i->trans, j->trans, data->skp_osg_node.coli_r)); + OsageCollision::osage_capsule_cls(coli_ring, i->trans, j->trans, data->skp_osg_node.coli_r) + + OsageCollision::osage_capsule_cls(coli, i->trans, j->trans, data->skp_osg_node.coli_r)); i->field_C8 += v17; j->field_C8 += v17; } if (coli_type && (coli_type != 1 || i != i_begin)) { float_t v20 = (float_t)( - coli->osage_capsule_cls(i[0].trans, i[-1].trans, data->skp_osg_node.coli_r) - + coli_ring->osage_capsule_cls(i[0].trans, i[-1].trans, data->skp_osg_node.coli_r)); + OsageCollision::osage_capsule_cls(coli_ring, i[0].trans, i[-1].trans, data->skp_osg_node.coli_r) + + OsageCollision::osage_capsule_cls(coli, i[0].trans, i[-1].trans, data->skp_osg_node.coli_r)); i[0].field_C8 += v20; i[-1].field_C8 += v20; } @@ -4433,12 +4003,12 @@ static void sub_14047ECA0(RobOsage* rob_osg, float_t step) { continue; for (RobOsageNode*& j : data->boc) { - j->field_C8 += (float_t)coli_ring->osage_cls(j->trans, data->skp_osg_node.coli_r); - j->field_C8 += (float_t)coli->osage_cls(j->trans, data->skp_osg_node.coli_r); + j->field_C8 += (float_t)OsageCollision::osage_cls(coli_ring, j->trans, data->skp_osg_node.coli_r); + j->field_C8 += (float_t)OsageCollision::osage_cls(coli, j->trans, data->skp_osg_node.coli_r); } - i->field_C8 += (float_t)coli_ring->osage_cls(i->trans, data->skp_osg_node.coli_r); - i->field_C8 += (float_t)coli->osage_cls(i->trans, data->skp_osg_node.coli_r); + i->field_C8 += (float_t)OsageCollision::osage_cls(coli_ring, i->trans, data->skp_osg_node.coli_r); + i->field_C8 += (float_t)OsageCollision::osage_cls(coli, i->trans, data->skp_osg_node.coli_r); } } @@ -4486,8 +4056,8 @@ static void sub_14047F990(RobOsage* rob_osg, mat4* a2, vec3* parent_scale, bool sub_140482F30(&v74, &j->sibling_node->trans, j->max_distance); skin_param_osage_node* v38 = &j->data_ptr->skp_osg_node; - coli_ring->osage_cls(v74, v38->coli_r); - coli->osage_cls(v74, v38->coli_r); + OsageCollision::osage_cls(coli_ring, v74, v38->coli_r); + OsageCollision::osage_cls(coli, v74, v38->coli_r); float_t v39 = v25 + v38->coli_r; if (v74.y < v39 && v39 < 1001.0f) { @@ -4528,8 +4098,8 @@ static void sub_14047F990(RobOsage* rob_osg, mat4* a2, vec3* parent_scale, bool } } -static void sub_140480260(RobOsage* rob_osg, mat4* mat, vec3* parent_scale, float_t step, bool v5) { - if (!v5) { +static void sub_140480260(RobOsage* rob_osg, mat4* mat, vec3* parent_scale, float_t step, bool disable_external_force) { + if (!disable_external_force) { rob_osg->SetNodesExternalForce(0, 1.0f); rob_osg->SetNodesForce(1.0f); rob_osg->set_external_force = false; diff --git a/src/CRE/rob/motion.cpp b/src/CRE/rob/motion.cpp new file mode 100644 index 00000000..748ab6bb --- /dev/null +++ b/src/CRE/rob/motion.cpp @@ -0,0 +1,176 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#include "motion.hpp" + +struct MotFile { + const motion_set_info* mot_set_info; + prj::shared_ptr alloc_handler; + mot_set* mot_set; + p_file_handler file_handler; + int32_t load_count; + + MotFile(); + virtual ~MotFile(); + + bool CheckNotReady(); + void FreeData(); + void LoadFile(std::string* mdata_dir, uint32_t set); + void ParseFile(const void* data, size_t size); + bool Unload(); + + static void ParseFileParent(MotFile* mot, const void* file_data, size_t size); +}; + +std::map motion_storage_data; + +void motion_set_load_motion(uint32_t set, std::string* mdata_dir, motion_database* mot_db) { + const motion_set_info* set_info = mot_db->get_motion_set_by_id(set); + if (!set_info) + return; + + auto elem = motion_storage_data.find(set); + if (elem == motion_storage_data.end()) + elem = motion_storage_data.insert({ set, {} }).first; + + elem->second.mot_set_info = set_info; + elem->second.LoadFile(mdata_dir, set); +} + +void motion_set_unload_motion(uint32_t set) { + auto elem = motion_storage_data.find(set); + if (elem != motion_storage_data.end() && elem->second.Unload()) + motion_storage_data.erase(elem); +} + +bool motion_storage_check_mot_file_not_ready(uint32_t set_id) { + auto elem = motion_storage_data.find(set_id); + if (elem != motion_storage_data.end()) + return elem->second.CheckNotReady(); + return false; +} + +const mot_data* motion_storage_get_mot_data(uint32_t motion_id, motion_database* mot_db) { + uint32_t set_id = -1; + size_t motion_index = -1; + for (motion_set_info& i : mot_db->motion_set) { + for (motion_info& j : i.motion) + if (j.id == motion_id) { + set_id = i.id; + motion_index = &j - i.motion.data(); + + break; + } + + if (set_id != -1) + break; + } + + if (set_id == -1) + return 0; + + auto elem = motion_storage_data.find(set_id); + if (elem != motion_storage_data.end()) + return &elem->second.mot_set->mot_data[motion_index]; + return 0; +} + +float_t motion_storage_get_mot_data_frame_count(uint32_t motion_id, motion_database* mot_db) { + const mot_data* mot = motion_storage_get_mot_data(motion_id, mot_db); + if (mot) + return mot->frame_count; + return 0.0f; +} + +const mot_set* motion_storage_get_motion_set(uint32_t set_id) { + auto elem = motion_storage_data.find(set_id); + if (elem != motion_storage_data.end()) + return elem->second.mot_set; + return 0; +} + +void motion_init() { + motion_storage_data = {}; +} + +void motion_free() { + motion_storage_data.clear(); +} + +MotFile::MotFile() : mot_set_info(), mot_set(), load_count() { + FreeData(); +} + +MotFile:: ~MotFile() { + FreeData(); +} + +bool MotFile::CheckNotReady() { + return file_handler.check_not_ready(); +} + +void MotFile::FreeData() { + mot_set_info = 0; + alloc_handler.reset(); + mot_set = 0; + file_handler.reset(); + load_count = 0; +} + +void MotFile::LoadFile(std::string* mdata_dir, uint32_t set) { + if (load_count > 0) { + load_count++; + return; + } + + data_struct* aft_data = &data_list[DATA_AFT]; + + std::string rom_dir = "rom/"; + std::string rob_dir = "rob/"; + std::string farc_file = "mot_" + mot_set_info->name + ".farc"; + std::string mot_file = "mot_" + mot_set_info->name + ".bin"; + if (mdata_dir && mdata_dir->size()) + if (aft_data->check_file_exists(mdata_dir->c_str(), farc_file.c_str())) { + rom_dir = *mdata_dir; + rob_dir.clear(); + } + + std::string dir = rom_dir + rob_dir; + if (aft_data->check_file_exists(dir.c_str(), farc_file.c_str())) { + if (file_handler.read_file(aft_data, dir.c_str(), farc_file.c_str(), mot_file.c_str(), false)) + file_handler.set_callback_data(0, (PFNFILEHANDLERCALLBACK*)ParseFileParent, this); + } + else { + if (file_handler.read_file(aft_data, rom_dir.c_str(), mot_file.c_str())) + file_handler.set_callback_data(0, (PFNFILEHANDLERCALLBACK*)ParseFileParent, this); + } + load_count = 1; +} + +void MotFile::ParseFile(const void* data, size_t size) { + prj::shared_ptr& alloc = alloc_handler; + alloc = prj::shared_ptr(new prj::stack_allocator); + + ::mot_set* set = alloc->allocate<::mot_set>(); + mot_set = set; + set->unpack_file(alloc, data, size, false); +} + +bool MotFile::Unload() { + if (--load_count < 0) { + load_count = 0; + return true; + } + else if (load_count <= 0) { + FreeData(); + return true; + } + else + return false; +} + +void MotFile::ParseFileParent(MotFile* mot, const void* file_data, size_t size) { + mot->ParseFile(file_data, size); +} diff --git a/src/CRE/rob/motion.hpp b/src/CRE/rob/motion.hpp new file mode 100644 index 00000000..3d60a7f3 --- /dev/null +++ b/src/CRE/rob/motion.hpp @@ -0,0 +1,19 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#pragma once + +#include "rob.hpp" + +extern void motion_set_load_motion(uint32_t set, std::string* mdata_dir, motion_database* mot_db); +extern void motion_set_unload_motion(uint32_t set); + +extern bool motion_storage_check_mot_file_not_ready(uint32_t set_id); +extern const mot_data* motion_storage_get_mot_data(uint32_t motion_id, motion_database* mot_db); +extern float_t motion_storage_get_mot_data_frame_count(uint32_t motion_id, motion_database* mot_db); +extern const mot_set* motion_storage_get_motion_set(uint32_t set_id); + +extern void motion_init(); +extern void motion_free(); diff --git a/src/CRE/rob/rob.cpp b/src/CRE/rob/rob.cpp index 500a9565..89894a02 100644 --- a/src/CRE/rob/rob.cpp +++ b/src/CRE/rob/rob.cpp @@ -7,36 +7,19 @@ #include "../../KKdLib/io/file_stream.hpp" #include "../../KKdLib/io/json.hpp" #include "../../KKdLib/io/path.hpp" +#include "../../KKdLib/prj/algorithm.hpp" #include "../../KKdLib/key_val.hpp" #include "../../KKdLib/msgpack.hpp" #include "../../KKdLib/sort.hpp" #include "../../KKdLib/str_utils.hpp" +#include "../../KKdLib/waitable_timer.hpp" #include "../data.hpp" -#include "../draw_task.hpp" #include "../pv_db.hpp" #include "../random.hpp" #include "../pv_expression.hpp" #include "../stage.hpp" -#include "../waitable_timer.hpp" - -struct MotFile { - motion_set_info* mot_set_info; - prj::shared_ptr alloc_handler; - mot_set* mot_set; - p_file_handler file_handler; - int32_t load_count; - - MotFile(); - virtual ~MotFile(); - - bool CheckNotReady(); - void FreeData(); - void LoadFile(std::string* mdata_dir, uint32_t set); - void ParseFile(const void* data, size_t size); - bool Unload(); - - static void ParseFileParent(MotFile* mot, const void* file_data, size_t size); -}; +#include "motion.hpp" +#include "skin_param.hpp" struct MhdFile { mothead* data; @@ -67,6 +50,7 @@ struct OpdMaker { Data(); ~Data(); + bool CheckOpdiFilesNotReady(); const void* GetOpdiFileData(object_info obj_info, int32_t motion_id); bool IsValidOpdiFile(rob_chara* rob_chr, int32_t motion_id); void Reset(); @@ -109,10 +93,12 @@ struct OpdMakeWorker : public app::Task { OpdMakeWorker(); virtual ~OpdMakeWorker() override; - bool Init() override; - bool Ctrl() override; - bool Dest() override; - void Disp() override; + virtual bool Init() override; + virtual bool Ctrl() override; + virtual bool Dest() override; + virtual void Disp() override; + + bool AddTask(bool a2); }; struct struc_528 { @@ -209,7 +195,7 @@ struct OpdMakeManager : app::Task { bool Dest() override; void Disp() override; - bool TaskFree(); + bool DelTask(); }; struct struc_380 { @@ -377,49 +363,6 @@ struct RobThreadHandler { void sub_14054E3F0(); }; -struct struc_461 { - p_file_handler* file_handler; - item_id id; - int32_t field_C; - std::vector* field_10; - rob_chara* rob_chr; -}; - -struct struc_462 { - rob_chara* rob_chr; - int32_t pv_id; - int32_t motion_id; - std::string name; - int32_t frame; - - struc_462(); - ~struc_462(); -}; - -struct SkinParamManager : public app::Task { - int32_t state; - uint8_t index; - std::vector field_70; - std::map>, std::vector*> field_88; - std::map>, std::vector*> field_98; - std::map>, std::vector*> field_A8; - std::list field_B8; - - SkinParamManager(); - virtual ~SkinParamManager() override; - - virtual bool Init() override; - virtual bool Ctrl() override; - virtual bool Dest() override; - virtual void Disp() override; - - void Reset(); - - void sub_14060FBD0(); - bool sub_140610480(std::vector& vec); - bool sub_1406112F0(); -}; - class TaskRobLoad : public app::Task { public: bool field_68; @@ -442,9 +385,9 @@ public: virtual bool Dest() override; bool AppendFreeReqData(chara_index chara_index); - bool AppendFreeReqDataObj(chara_index chara_index, item_cos_data* cos); + bool AppendFreeReqDataObj(chara_index chara_index, const item_cos_data* cos); bool AppendLoadReqData(chara_index chara_index); - bool AppendLoadReqDataObj(chara_index chara_index, item_cos_data* cos); + bool AppendLoadReqDataObj(chara_index chara_index, const item_cos_data* cos); void AppendLoadedReqData(ReqData* req_data); void AppendLoadedReqDataObj(ReqDataObj* req_data_obj); int32_t CtrlFunc1(); @@ -643,11 +586,9 @@ static void bone_data_mult_ik(bone_data* a1, int32_t skeleton_select); static void bone_data_parent_data_init(bone_data_parent* bone, rob_chara_bone_data* rob_bone_data, bone_database* bone_data); static void bone_data_parent_load_bone_database(bone_data_parent* bone, - std::vector* bones, vec3* common_translation, vec3* translation); + const std::vector* bones, const vec3* common_translation, const vec3* translation); static void bone_data_parent_load_rob_chara(bone_data_parent* bone); -static bool check_module_index_is_501(int32_t module_index); - static void mot_blend_interpolate(mot_blend* a1, std::vector* bones, std::vector* bone_indices, bone_database_skeleton_type skeleton_type); static void mot_blend_load_motion(mot_blend* a1, int32_t motion_id, motion_database* mot_db); @@ -660,7 +601,7 @@ static uint32_t mot_load_last_key_calc(uint16_t keys_count); static bool mot_load_file(mot* a1, const mot_data* a2); static void mot_key_data_get_key_set_count_by_bone_database_bones(mot_key_data* a1, - std::vector* a2); + const std::vector* a2); static void mot_key_data_get_key_set_count(mot_key_data* a1, size_t motion_bone_count, size_t ik_bone_count); static void mot_key_data_init_key_sets(mot_key_data* a1, bone_database_skeleton_type type, size_t motion_bone_count, size_t ik_bone_count); @@ -869,7 +810,7 @@ static void rob_disp_rob_chara_free(rob_chara* rob_chr); static void rob_motion_modifier_rob_chara_ctrl(rob_chara* rob_chr); static void rob_chara_bone_data_calculate_bones(rob_chara_bone_data* rob_bone_data, - std::vector* bones); + const std::vector* bones); static void rob_chara_bone_data_eyes_xrot_adjust(rob_chara_bone_data* rob_bone_data, const struc_241* a2, eyes_adjust* a3); static void rob_chara_bone_data_get_ik_scale( @@ -928,7 +869,7 @@ static void rob_chara_bone_data_set_hand_r_anim_blend_duration(rob_chara_bone_da static void rob_chara_bone_data_set_hand_r_frame(rob_chara_bone_data* rob_bone_data, float_t frame); static void rob_chara_bone_data_set_hand_r_play_frame_step(rob_chara_bone_data* rob_bone_data, float_t step); static void rob_chara_bone_data_set_mats(rob_chara_bone_data* rob_bone_data, - std::vector* bones, std::string* motion_bones); + const std::vector* bones, const std::string* motion_bones); static void rob_chara_bone_data_set_motion_duration(rob_chara_bone_data* rob_bone_data, float_t duration, float_t step, float_t offset); static void rob_chara_bone_data_set_mouth_anim_blend_duration(rob_chara_bone_data* rob_bone_data, @@ -936,65 +877,11 @@ static void rob_chara_bone_data_set_mouth_anim_blend_duration(rob_chara_bone_dat static void rob_chara_bone_data_set_mouth_frame(rob_chara_bone_data* rob_bone_data, float_t frame); static void rob_chara_bone_data_set_mouth_play_frame_step(rob_chara_bone_data* rob_bone_data, float_t step); static void rob_chara_bone_data_set_parent_mats(rob_chara_bone_data* rob_bone_data, - uint16_t* parent_indices); + const uint16_t* parent_indices); static void rob_chara_bone_data_set_rot_y(rob_chara_bone_data* rob_bone_data, float_t rot_y); +static void rob_chara_bone_data_set_step(rob_chara_bone_data* rob_bone_data, float_t step); static void rob_chara_bone_data_update(rob_chara_bone_data* rob_bone_data, mat4* mat); -static bool rob_chara_item_cos_data_check_for_npr_flag(rob_chara_item_cos_data* item_cos_data); -static object_info rob_chara_item_cos_data_get_head_object_replace( - rob_chara_item_cos_data* item_cos_data, int32_t head_object_id); -static float_t rob_chara_item_cos_data_get_max_face_depth(rob_chara_item_cos_data* item_cos_data); -static void rob_chara_item_cos_data_reload_items(rob_chara_item_cos_data* item_cos_data, - int32_t chara_id, bone_database* bone_data, void* data, object_database* obj_db); -static void rob_chara_item_cos_data_set_chara_index( - rob_chara_item_cos_data* item_cos_data, chara_index chara_index); -static void rob_chara_item_cos_data_set_chara_index_item_nos( - rob_chara_item_cos_data* item_cos_data, chara_index chara_index, int32_t* items); -static void rob_chara_item_cos_data_set_item(rob_chara_item_cos_data* item_cos_data, int32_t item_no); -static void rob_chara_item_cos_data_set_item_array(rob_chara_item_cos_data* item_cos_data, - rob_chara_pv_data_item* item); -static void rob_chara_item_cos_data_set_item_no( - rob_chara_item_cos_data* item_cos_data, item_sub_id sub_id, int32_t item_no); -static void rob_chara_item_cos_data_set_item_nos( - rob_chara_item_cos_data* item_cos_data, int32_t* item_nos); -static void rob_chara_item_cos_data_set_item_zero(rob_chara_item_cos_data* item_cos_data, int32_t item_no); -static void rob_chara_item_cos_data_texture_change_clear(rob_chara_item_cos_data* item_cos_data); -static void rob_chara_item_cos_data_texture_pattern_clear(rob_chara_item_cos_data* item_cos_data); - -static void rob_chara_item_equip_add_motion_reset_data(rob_chara_item_equip* rob_itm_equip, - int32_t motion_id, float_t frame, int32_t iterations); -static void rob_chara_item_equip_disp( - rob_chara_item_equip* rob_itm_equip, int32_t chara_id, render_context* rctx); -static object_info rob_chara_item_equip_get_object_info( - rob_chara_item_equip* rob_itm_equip, item_id id); -static void rob_chara_item_equip_get_parent_bone_nodes( - rob_chara_item_equip* rob_itm_equip, bone_node* bone_nodes, bone_database* bone_data); -static void rob_chara_item_equip_load_object_info( - rob_chara_item_equip* rob_itm_equip, object_info object_info, item_id id, - bool osage_reset, bone_database* bone_data, void* data, object_database* obj_db); -static void rob_chara_item_equip_reset_init_data(rob_chara_item_equip* rob_itm_equip, - bone_node* bone_nodes); -static void rob_chara_item_equip_set_alpha_draw_task_flags( - rob_chara_item_equip* rob_itm_equip, float_t alpha, draw_task_flags flags); -static void rob_chara_item_equip_set_item_equip_range(rob_chara_item_equip* rob_itm_equip, bool value); -static void rob_chara_item_equip_set_motion_reset_data( - rob_chara_item_equip* rob_itm_equip, int32_t motion_id, float_t frame); -static void rob_chara_item_equip_set_motion_skin_param( - rob_chara_item_equip* rob_itm_equip, int8_t chara_id, int32_t motion_id, int32_t frame); -static void rob_chara_item_equip_set_null_blocks_expression_data( - rob_chara_item_equip* rob_itm_equip, item_id id, vec3* position, vec3* rotation, vec3* scale); -static void rob_chara_item_equip_set_object_texture_pattern(rob_chara_item_equip* rob_itm_equip, - item_id id, texture_pattern_struct* tex_pat, size_t count); -static void rob_chara_item_equip_set_opd_blend_data( - rob_chara_item_equip* rob_itm_equip, std::list* a2); -static void rob_chara_item_equip_set_osage_reset(rob_chara_item_equip* rob_itm_equip); -static void rob_chara_item_equip_set_osage_move_cancel(rob_chara_item_equip* rob_itm_equip, - uint8_t id, float_t value); -static void rob_chara_item_equip_set_osage_step(rob_chara_item_equip* rob_itm_equip, float_t value); -static void rob_chara_item_equip_set_shadow_type(rob_chara_item_equip* rob_itm_equip, int32_t chara_id); -static void rob_chara_item_equip_set_texture_pattern(rob_chara_item_equip* rob_itm_equip, - texture_pattern_struct* tex_pat, size_t count); - static bool rob_chara_check_for_ageageagain_module(chara_index chara_index, int32_t module_index); static object_info rob_chara_get_head_object(rob_chara* rob_chr, int32_t head_object_id); static object_info rob_chara_get_object_info(rob_chara* rob_chr, item_id id); @@ -1013,15 +900,11 @@ static void rob_cmn_mottbl_read(void* a1, const void* data, size_t size); static rob_manager_rob_impl* rob_manager_rob_impls1_get(TaskRobManager* rob_mgr); static rob_manager_rob_impl* rob_manager_rob_impls2_get(TaskRobManager* rob_mgr); -static void skin_param_manager_reset(int32_t chara_id); - -static bool task_rob_load_check_load_req_data(); - -std::map motion_storage_data; -std::map mothead_storage_data; rob_chara* rob_chara_array; rob_chara_pv_data* rob_chara_pv_data_array; +std::map mothead_storage_data; + rob_manager_rob_impl rob_manager_rob_impls1[2]; bool rob_manager_rob_impls1_init = false; rob_manager_rob_impl rob_manager_rob_impls2[7]; @@ -1032,7 +915,6 @@ OpdMaker* opd_maker_array; PvOsageManager* pv_osage_manager_array; rob_cmn_mottbl_header* rob_cmn_mottbl_data; RobThreadHandler* rob_thread_handler; -SkinParamManager* skin_param_manager; TaskRobLoad* task_rob_load; TaskRobManager* task_rob_manager; @@ -2103,12 +1985,18 @@ float_t chara_pos_adjust_y_table_get_value(uint32_t index) { return 0.0f; } +bool check_module_index_is_501(int32_t module_index) { + if (module_index == -1 || module_index >= 502) + return false; + return module_index == 501; +} + const float_t get_osage_gravity_const() { return 0.00299444468691945f; } void motion_set_load_mothead(uint32_t set, std::string* mdata_dir, motion_database* mot_db) { - motion_set_info* set_info = mot_db->get_motion_set_by_id(set); + const motion_set_info* set_info = mot_db->get_motion_set_by_id(set); if (!set_info) return; @@ -2127,33 +2015,14 @@ void motion_set_load_mothead(uint32_t set, std::string* mdata_dir, motion_databa elem->second.LoadFile(path.c_str(), file.c_str(), set); } -void motion_set_load_motion(uint32_t set, std::string* mdata_dir, motion_database* mot_db) { - motion_set_info* set_info = mot_db->get_motion_set_by_id(set); - if (!set_info) - return; - - auto elem = motion_storage_data.find(set); - if (elem == motion_storage_data.end()) - elem = motion_storage_data.insert({ set, {} }).first; - - elem->second.mot_set_info = set_info; - elem->second.LoadFile(mdata_dir, set); -} - -void motion_set_unload_motion(uint32_t set) { - auto elem = motion_storage_data.find(set); - if (elem != motion_storage_data.end() && elem->second.Unload()) - motion_storage_data.erase(elem); -} - void motion_set_unload_mothead(uint32_t set) { auto elem = mothead_storage_data.find(set); if (elem != mothead_storage_data.end() && elem->second.Unload()) mothead_storage_data.erase(elem); } -bool opd_make_manager_task_free() { - return opd_make_manager->TaskFree(); +bool opd_make_manager_del_task() { + return opd_make_manager->DelTask(); } void pv_osage_manager_array_reset(int32_t chara_id) { @@ -2187,9 +2056,6 @@ void rob_init() { if (!rob_thread_handler) rob_thread_handler = new RobThreadHandler; - if (!skin_param_manager) - skin_param_manager = new SkinParamManager[ROB_CHARA_COUNT]; - if (!task_rob_load) task_rob_load = new TaskRobLoad; @@ -2198,41 +2064,9 @@ void rob_init() { } void rob_free() { - if (opd_make_manager) { - delete opd_make_manager; - opd_make_manager = 0; - } - - if (opd_maker_array) { - delete[] opd_maker_array; - opd_maker_array = 0; - } - - if (pv_osage_manager_array) { - delete[] pv_osage_manager_array; - pv_osage_manager_array = 0; - } - - if (rob_chara_array) { - delete[] rob_chara_array; - rob_chara_array = 0; - } - - if (rob_chara_pv_data_array) { - delete[] rob_chara_pv_data_array; - rob_chara_pv_data_array = 0; - } - - free_def(rob_cmn_mottbl_data); - - if (rob_thread_handler) { - delete rob_thread_handler; - rob_thread_handler = 0; - } - - if (skin_param_manager) { - delete[] skin_param_manager; - skin_param_manager = 0; + if (task_rob_manager) { + delete task_rob_manager; + task_rob_manager = 0; } if (task_rob_load) { @@ -2240,13 +2074,41 @@ void rob_free() { task_rob_load = 0; } - if (task_rob_manager) { - delete task_rob_manager; - task_rob_manager = 0; + if (rob_thread_handler) { + delete rob_thread_handler; + rob_thread_handler = 0; + } + + free_def(rob_cmn_mottbl_data); + + if (rob_chara_pv_data_array) { + delete[] rob_chara_pv_data_array; + rob_chara_pv_data_array = 0; + } + + if (rob_chara_array) { + delete[] rob_chara_array; + rob_chara_array = 0; + } + + if (pv_osage_manager_array) { + delete[] pv_osage_manager_array; + pv_osage_manager_array = 0; + } + + if (opd_maker_array) { + delete[] opd_maker_array; + opd_maker_array = 0; + } + + if (opd_make_manager) { + delete opd_make_manager; + opd_make_manager = 0; } } -static void rob_chara_item_equip_object_ctrl_iterate_nodes(rob_chara_item_equip_object* itm_eq_obj, int32_t osage_iterations) { +static void rob_chara_item_equip_object_ctrl_iterate_nodes( + rob_chara_item_equip_object* itm_eq_obj, int32_t osage_iterations) { if (!itm_eq_obj->node_blocks.size()) return; @@ -2419,7 +2281,7 @@ float_t rob_chara::get_frame_count() { } float_t rob_chara::get_max_face_depth() { - return rob_chara_item_cos_data_get_max_face_depth(&item_cos_data); + return item_cos_data.get_max_face_depth(); } static int16_t sub_14054FE90(rob_chara* rob_chr, bool a3) { @@ -2938,7 +2800,7 @@ void rob_chara::load_motion(int32_t motion_id, bool a3, float_t frame, } rob_chara_bone_data_set_disable_eye_motion(this->bone_data, false); - rob_chara_item_equip_set_osage_move_cancel(item_equip, 0, 0.0f); + item_equip->set_osage_move_cancel(0, 0.0f); for (int32_t i = 0; i < ROB_OSAGE_PARTS_MAX; i++) sub_140513EE0(item_equip, (rob_osage_parts)i, false); data.field_1588.field_0.field_30 = data.field_1588.field_0.field_10; @@ -2966,7 +2828,7 @@ void rob_chara::load_motion(int32_t motion_id, bool a3, float_t frame, float_t step = data.motion.step; if (step < 0.0f) step = data.motion.step_data.frame; - rob_chara_item_equip_set_osage_step(item_equip, step); + item_equip->set_step(step); rob_chara_bone_data_motion_load(this->bone_data, motion_id, blend_type, bone_data, mot_db); this->bone_data->set_motion_frame(data.motion.step_data.frame, data.motion.step_data.frame, data.field_1588.field_0.frame_count); @@ -2998,6 +2860,11 @@ void rob_chara::load_motion(int32_t motion_id, bool a3, float_t frame, data.field_3DE0 = 0; } +void rob_chara::load_outfit_object_info(item_id id, object_info obj_info, + bool osage_reset, bone_database* bone_data, void* data, object_database* obj_db) { + item_equip->load_outfit_object_info(id, obj_info, osage_reset, bone_data, data, obj_db); +} + static void sub_140514130(rob_chara_item_equip* rob_itm_equip, item_id id, bool a3) { if (a3) rob_itm_equip->field_18[id] &= ~0x01; @@ -3026,8 +2893,7 @@ void rob_chara::reset_data(rob_chara_pv_data* pv_data, BONE_DATABASE_SKELETON_COMMON, chara_init_data->skeleton_type, bone_data); data.reset(); data_prev.reset(); - rob_chara_item_equip_get_parent_bone_nodes(item_equip, - rob_chara_bone_data_get_node(this->bone_data, 0), bone_data); + item_equip->get_parent_bone_nodes(rob_chara_bone_data_get_node(this->bone_data, 0), bone_data); type = pv_data->type; rob_chara_data* rob_chr_data = &data; rob_chr_data->miku_rot.rot_y_int16 = pv_data->rot_y_int16; @@ -3072,7 +2938,7 @@ void rob_chara::reset_osage() { item_equip->parts_append = false; item_equip->parts_white_one_l = false; - set_osage_step(-1.0f); + set_step(-1.0f); } void rob_chara::set_bone_data_frame(float_t frame) { @@ -3557,9 +3423,10 @@ void rob_chara::set_face_mottbl_motion(int32_t type, void rob_chara::set_frame(float_t frame) { bone_data->set_frame(frame); - rob_chara_bone_data_interpolate(bone_data); - rob_chara_bone_data_update(bone_data, 0); - sub_140509D30(this); + data.motion.frame_data.frame = frame; + //rob_chara_bone_data_interpolate(bone_data); + //rob_chara_bone_data_update(bone_data, 0); + //sub_140509D30(this); } void rob_chara::set_hand_l_mottbl_motion(int32_t type, @@ -3637,12 +3504,11 @@ bool rob_chara::set_motion_id(int32_t motion_id, } void rob_chara::set_motion_reset_data(int32_t motion_id, float_t frame) { - rob_chara_item_equip_set_motion_reset_data(item_equip, motion_id, frame); + item_equip->set_motion_reset_data(motion_id, frame); } void rob_chara::set_motion_skin_param(int32_t motion_id, float_t frame) { - rob_chara_item_equip_set_motion_skin_param(item_equip, - chara_id, motion_id, (int32_t)roundf(frame)); + item_equip->set_motion_skin_param(chara_id, motion_id, (int32_t)roundf(frame)); } void rob_chara::set_mouth_mottbl_motion(int32_t type, @@ -3669,7 +3535,7 @@ void rob_chara::set_mouth_mottbl_motion(int32_t type, } void rob_chara::set_osage_move_cancel(uint8_t id, float_t value) { - rob_chara_item_equip_set_osage_move_cancel(item_equip, id, value); + item_equip->set_osage_move_cancel(id, value); /*if (id <= 1) { sub_1405436A0(chara_id, 1, value); sub_1405436A0(chara_id, 2, value); @@ -3677,14 +3543,7 @@ void rob_chara::set_osage_move_cancel(uint8_t id, float_t value) { } void rob_chara::set_osage_reset() { - rob_chara_item_equip_set_osage_reset(item_equip); -} - -void rob_chara::set_osage_step(float_t value) { - data.motion.step = value; - if (value < 0.0f) - value = data.motion.step_data.frame; - rob_chara_item_equip_set_osage_step(item_equip, value); + item_equip->set_osage_reset(); } void rob_chara::set_parts_disp(item_id id, bool disp) { @@ -3705,6 +3564,28 @@ void rob_chara::set_parts_disp(item_id id, bool disp) { } } +void rob_chara::set_step(float_t value) { + data.motion.step = value; + if (value < 0.0f) + value = data.motion.step_data.frame; + item_equip->set_step(value); +} + +void rob_chara::set_step_motion_step(float_t step) { + data.motion.step_data.step = step; + if (step < 0.0f) + data.motion.step_data.frame = 1.0f; + else + data.motion.step_data.frame = step; + + rob_chara_bone_data_set_step(bone_data, data.motion.step_data.frame); + + step = data.motion.step; + if (step < 0.0f) + step = data.motion.step_data.frame; + item_equip->set_step(step); +} + void rob_chara::set_visibility(bool value) { if (value) { data.field_0 |= 0x01; @@ -3720,6 +3601,23 @@ void rob_chara::set_visibility(bool value) { }*/ } +osage_init_data::osage_init_data() : rob_chr() { + pv_id = -1; + motion_id = -1; + frame = -1; +} + +osage_init_data::osage_init_data(rob_chara* rob_chr, int32_t motion_id) { + this->rob_chr = rob_chr; + pv_id = -1; + this->motion_id = motion_id; + frame = -1; +} + +osage_init_data::~osage_init_data() { + +} + static float_t bone_data_limit_angle(float_t angle) { bool neg; if (angle < 0.0f) { @@ -4281,9 +4179,9 @@ static void bone_data_parent_data_init(bone_data_parent* bone, rob_chara_bone_data* rob_bone_data, bone_database* bone_data) { const char* base_name = bone_database_skeleton_type_to_string(rob_bone_data->base_skeleton_type); const char* name = bone_database_skeleton_type_to_string(rob_bone_data->skeleton_type); - std::vector* common_bones = 0; - std::vector* common_translation = 0; - std::vector* translation = 0; + const std::vector* common_bones = 0; + const std::vector* common_translation = 0; + const std::vector* translation = 0; if (!bone_data->get_skeleton_bones(base_name, &common_bones) || !bone_data->get_skeleton_positions(base_name, &common_translation) || !bone_data->get_skeleton_positions(name, &translation)) @@ -4295,14 +4193,14 @@ static void bone_data_parent_data_init(bone_data_parent* bone, } static void bone_data_parent_load_bone_database(bone_data_parent* bone, - std::vector* bones, vec3* common_translation, vec3* translation) { + const std::vector* bones, const vec3* common_translation, const vec3* translation) { rob_chara_bone_data* rob_bone_data = bone->rob_bone_data; size_t chain_pos = 0; size_t total_bone_count = 0; size_t ik_bone_count = 0; bone_data* bone_node = bone->bones.data(); - for (bone_database_bone& i : *bones ) { + for (const bone_database_bone& i : *bones ) { bone_node->motion_bone_index = (motion_bone_index)(&i - bones->data()); bone_node->type = i.type; bone_node->mirror = i.mirror; @@ -4411,17 +4309,12 @@ static void bone_data_parent_load_rob_chara(bone_data_parent* bone) { bone->ik_bone_count = rob_bone_data->ik_bone_count; bone->chain_pos = rob_bone_data->chain_pos; bone->bones.clear(); + bone->bones.shrink_to_fit(); bone->bones.resize(rob_bone_data->motion_bone_count); bone->global_key_set_count = 6; bone->bone_key_set_count = (uint32_t)((bone->motion_bone_count + bone->ik_bone_count) * 3); } -static bool check_module_index_is_501(int32_t module_index) { - if (module_index == -1 || module_index >= 502) - return false; - return module_index == 501; -} - static bool sub_140410250(struc_313* a1, size_t a2) { return !!(a1->bitfield.data()[a2 >> 5] & (1 << (a2 & 0x1F))); } @@ -4716,7 +4609,7 @@ static bool mot_load_file(mot* a1, const mot_data* a2) { } static void mot_key_data_get_key_set_count_by_bone_database_bones(mot_key_data* a1, - std::vector* a2) { + const std::vector* a2) { size_t object_bone_count; size_t motion_bone_count; size_t total_bone_count; @@ -5274,7 +5167,7 @@ static void mothead_func_32(mothead_func_data* func_data, if (rob_chr_data->motion.field_28 & 0x08) v5 = sub_140533440(v5); - float_t v7 = (float_t)(rob_chr_data->field_1588.field_0.field_8 - 1.0f) + float_t v7 = rob_chr_data->field_1588.field_0.field_8 - 1.0f - rob_chr_data->motion.frame_data.frame; if (v7 >= v8) v7 = v8; @@ -5591,7 +5484,7 @@ static void mothead_func_65_motion_skin_param(mothead_func_data* func_data, static void mothead_func_66_osage_step(mothead_func_data* func_data, void* data, const mothead_data* mhd_data, int32_t frame, motion_database* mot_db) { - func_data->rob_chr->set_osage_step(((float_t*)data)[0]); + func_data->rob_chr->set_step(((float_t*)data)[0]); } static void mothead_func_67_sleeve(mothead_func_data* func_data, @@ -7493,20 +7386,6 @@ static object_info sub_140550330(rob_chara* rob_chr) { return rob_chr->data.motion.field_150.head_object; } -static void sub_140513950( - rob_chara_item_equip* rob_itm_equip, item_id id, object_info object_info, bool a4, - bone_database* bone_data, void* data, object_database* obj_db) { - if ((uint32_t)(id - 1) > 13) - object_info = ::object_info(); - rob_chara_item_equip_load_object_info(rob_itm_equip, object_info, id, a4, bone_data, data, obj_db); -} - -static void sub_140552310(rob_chara* rob_chr, item_id id, - object_info object_info, bool a4, bone_database* bone_data, void* data, object_database* obj_db) { - sub_140513950(rob_chr->item_equip, - id, object_info, a4, bone_data, data, obj_db); -} - static object_info sub_140550350(rob_chara* rob_chr){ if (rob_chr->data.motion.field_29 & 0x08) return rob_chr->data.motion.field_3B0.hand_l_object; @@ -7646,7 +7525,8 @@ static void sub_140504710(rob_chara* rob_chr, motion_database* mot_db, object_info v8 = sub_140550330(rob_chr); if (rob_chara_get_object_info(rob_chr, ITEM_ATAMA) != v8) - sub_140552310(rob_chr, ITEM_ATAMA, v8, false, bone_data, data, obj_db); + rob_chr->load_outfit_object_info(ITEM_ATAMA, + v8, false, bone_data, data, obj_db); object_info v9 = rob_chara_get_head_object(rob_chr, 1); object_info v10 = rob_chara_get_head_object(rob_chr, 7); @@ -7656,15 +7536,18 @@ static void sub_140504710(rob_chara* rob_chr, motion_database* mot_db, object_info v12 = sub_140550350(rob_chr); if (rob_chara_get_object_info(rob_chr, ITEM_TE_L) != v12) - sub_140552310(rob_chr, ITEM_TE_L, v12, true, bone_data, data, obj_db); + rob_chr->load_outfit_object_info(ITEM_TE_L, + v12, true, bone_data, data, obj_db); object_info v13 = sub_140550380(rob_chr); if (rob_chara_get_object_info(rob_chr, ITEM_TE_R) != v13) - sub_140552310(rob_chr, ITEM_TE_R, v13, true, bone_data, data, obj_db); + rob_chr->load_outfit_object_info(ITEM_TE_R, + v13, true, bone_data, data, obj_db); object_info v14 = sub_140550310(rob_chr); if (rob_chara_get_object_info(rob_chr, ITEM_HARA) != v14) - sub_140552310(rob_chr, ITEM_HARA, v14, false, bone_data, data, obj_db); + rob_chr->load_outfit_object_info(ITEM_HARA, + v14, false, bone_data, data, obj_db); rob_chara_bone_data_interpolate(rob_chr->bone_data); rob_chara_bone_data_update(rob_chr->bone_data, 0); @@ -7770,18 +7653,23 @@ static void rob_base_rob_chara_free(rob_chara* rob_chr) { static void rob_disp_rob_chara_init(rob_chara* rob_chr, bone_database* bone_data, void* data, object_database* obj_db) { + data_struct* aft_data = &data_list[DATA_AFT]; + bone_database* aft_bone_data = &aft_data->data_ft.bone_data; + object_database* aft_obj_db = &aft_data->data_ft.obj_db; + + rob_chara_item_equip* rob_itm_equip = rob_chr->item_equip; + const chara_init_data* chr_init_data = rob_chr->chara_init_data; bone_node* v3 = rob_chara_bone_data_get_node(rob_chr->bone_data, 0); - rob_chara_item_equip_reset_init_data(rob_chr->item_equip, v3); - rob_chara_item_equip_set_item_equip_range(rob_chr->item_equip, rob_chr->module_index == 501); - rob_chara_item_equip_load_object_info(rob_chr->item_equip, - object_info(), ITEM_BODY, false, bone_data, data, obj_db); - rob_chara_item_equip_load_object_info(rob_chr->item_equip, - rob_chr->chara_init_data->field_7E4[0], ITEM_ATAMA, false, bone_data, data, obj_db); - rob_chara_item_equip_set_shadow_type(rob_chr->item_equip, rob_chr->chara_id); + rob_itm_equip->reset_init_data(v3); + rob_itm_equip->set_item_equip_range(rob_chr->module_index == 501); + rob_itm_equip->load_object_info({}, ITEM_BODY, false, bone_data, data, obj_db); + for (int32_t i = ITEM_BODY; i < ITEM_ASI; i++) + rob_itm_equip->load_outfit_object_info((item_id)i, + chr_init_data->field_7E4[i], false, aft_bone_data, aft_data, aft_obj_db); + rob_itm_equip->set_shadow_type(rob_chr->chara_id); rob_chr->item_equip->field_A0 = 5; - rob_chara_item_cos_data_reload_items(&rob_chr->item_cos_data, - rob_chr->chara_id, bone_data, data, obj_db); - if (rob_chara_item_cos_data_check_for_npr_flag(&rob_chr->item_cos_data)) + rob_chr->item_cos_data.reload_items(rob_chr->chara_id, bone_data, data, obj_db); + if (rob_chr->item_cos_data.check_for_npr_flag()) rob_chr->item_equip->npr_flag = true; /*if (rob_chara_check_for_ageageagain_module(rob_chrchara_index, rob_chr->module_index)) { @@ -7826,7 +7714,7 @@ static void rob_disp_rob_chara_ctrl_thread_main(rob_chara* rob_chr) { if (rob_chr->bone_data->get_frame() > frame_count) rob_chr->bone_data->set_frame(frame_count); - rob_chara_item_equip_set_opd_blend_data(rob_chr->item_equip, &rob_chr->bone_data->motion_loaded); + rob_chr->item_equip->set_opd_blend_data(&rob_chr->bone_data->motion_loaded); vec3 trans = 0.0f; rob_chara_get_trans_scale(rob_chr, 0, &trans); @@ -7841,9 +7729,9 @@ static void rob_disp_rob_chara_ctrl_thread_main(rob_chara* rob_chr) { } static void rob_disp_rob_chara_disp(rob_chara* rob_chr) { - rob_chr->item_equip->texture_color_coeff = 1.0f; + rob_chr->item_equip->texture_color_coefficients = 1.0f; rob_chr->item_equip->mat = rob_chr->data.adjust_data.mat; - rob_chara_item_equip_disp(rob_chr->item_equip, rob_chr->chara_id, rctx_ptr); + rob_chr->item_equip->disp(rob_chr->chara_id, rctx_ptr); } static void rob_disp_rob_chara_free(rob_chara* rob_chr) { @@ -7884,7 +7772,7 @@ static void sub_140409B70(struc_258* a1, vec3* a2, mat4* a3, bool a4) { if (a4 && a1->field_A0 > 0.0f && a1->field_A0 > a1->field_AC && fabsf(a1->field_A0 - a1->field_AC) > 0.000001f) { - a1->field_AC = a1->field_AC + a1->field_A8; + a1->field_AC += a1->field_A8; a1->field_198 = (a1->field_AC + 1.0f) / (a1->field_A0 + 1.0f); } else { @@ -9320,8 +9208,7 @@ static void sub_1404065B0(vec3* a1, std::vector* a2, mat4* a3, float_ bone_database* aft_bone_data = &aft_data->data_ft.bone_data; const char* name = bone_database_skeleton_type_to_string(skeleton_type); - float_t* base_heel_height = 0; - float_t* heel_height = 0; + const float_t* heel_height = 0; if (!aft_bone_data->get_skeleton_heel_height(name, &heel_height)) return; @@ -9455,7 +9342,7 @@ static void rob_motion_modifier_rob_chara_ctrl(rob_chara* rob_chr) { } static void rob_chara_bone_data_calculate_bones(rob_chara_bone_data* rob_bone_data, - std::vector* bones) { + const std::vector* bones) { bone_database_bones_calculate_count(bones, &rob_bone_data->object_bone_count, &rob_bone_data->motion_bone_count, &rob_bone_data->total_bone_count, &rob_bone_data->ik_bone_count, &rob_bone_data->chain_pos); @@ -9537,8 +9424,8 @@ static void rob_chara_bone_data_ik_scale_calculate( bone_database_skeleton_type skeleton_type, bone_database* bone_data) { const char* base_name = bone_database_skeleton_type_to_string(base_skeleton_type); const char* name = bone_database_skeleton_type_to_string(skeleton_type); - float_t* base_heel_height = 0; - float_t* heel_height = 0; + const float_t* base_heel_height = 0; + const float_t* heel_height = 0; if (!bone_data->get_skeleton_heel_height(base_name, &base_heel_height) || !bone_data->get_skeleton_heel_height(name, &heel_height)) return; @@ -9564,7 +9451,7 @@ static void rob_chara_bone_data_ik_scale_calculate( static void mot_key_data_reserve_key_sets_by_skeleton_type(mot_key_data* a1, bone_database_skeleton_type type, bone_database* bone_data) { const char* name = bone_database_skeleton_type_to_string(type); - std::vector* bones = 0; + const std::vector* bones = 0; if (!bone_data->get_skeleton_bones(name, &bones)) return; @@ -9643,9 +9530,9 @@ static void rob_chara_bone_data_init_skeleton(rob_chara_bone_data* rob_bone_data return; const char* name = bone_database_skeleton_type_to_string(base_skeleton_type); - std::vector* bones = 0; - std::vector* parent_indices = 0; - std::vector* motion_bones = 0; + const std::vector* bones = 0; + const std::vector* parent_indices = 0; + const std::vector* motion_bones = 0; if (!bone_data->get_skeleton_bones(name, &bones) || !bone_data->get_skeleton_parent_indices(name, &parent_indices) || !bone_data->get_skeleton_motion_bones(name, &motion_bones)) @@ -9926,8 +9813,22 @@ static void rob_chara_bone_data_motion_blend_mot_list_init(rob_chara_bone_data* } static void rob_chara_bone_data_reserve(rob_chara_bone_data* rob_bone_data) { + rob_bone_data->mats.clear(); + rob_bone_data->mats.shrink_to_fit(); rob_bone_data->mats.resize(rob_bone_data->object_bone_count); + + for (mat4& i : rob_bone_data->mats) + i = mat4_identity; + + rob_bone_data->mats2.clear(); + rob_bone_data->mats2.shrink_to_fit(); rob_bone_data->mats2.resize(rob_bone_data->total_bone_count - rob_bone_data->object_bone_count); + + for (mat4& i : rob_bone_data->mats2) + i = mat4_identity; + + rob_bone_data->nodes.clear(); + rob_bone_data->nodes.shrink_to_fit(); rob_bone_data->nodes.resize(rob_bone_data->total_bone_count); } @@ -10221,7 +10122,7 @@ static void rob_chara_bone_data_set_hand_r_play_frame_step(rob_chara_bone_data* } static void rob_chara_bone_data_set_mats(rob_chara_bone_data* rob_bone_data, - std::vector* bones, std::string* motion_bones) { + const std::vector* bones, const std::string* motion_bones) { size_t chain_pos = 0; size_t ik_bone_count = 0; size_t total_bone_count = 0; @@ -10229,7 +10130,7 @@ static void rob_chara_bone_data_set_mats(rob_chara_bone_data* rob_bone_data, mat4* mats = rob_bone_data->mats.data(); mat4* mats2 = rob_bone_data->mats2.data(); bone_node* nodes = rob_bone_data->nodes.data(); - for (bone_database_bone& i : *bones) + for (const bone_database_bone& i : *bones) switch (i.type) { case BONE_DATABASE_BONE_ROTATION: case BONE_DATABASE_BONE_TYPE_1: @@ -10325,21 +10226,27 @@ static void rob_chara_bone_data_set_mouth_play_frame_step(rob_chara_bone_data* r } static void rob_chara_bone_data_set_parent_mats(rob_chara_bone_data* rob_bone_data, - uint16_t* parent_indices) { + const uint16_t* parent_indices) { if (rob_bone_data->nodes.size() < 1) return; parent_indices++; - bone_node* i_begin = rob_bone_data->nodes.data() + 1; + bone_node* i_begin = rob_bone_data->nodes.data(); + bone_node* i_begin_1 = rob_bone_data->nodes.data() + 1; bone_node* i_end = rob_bone_data->nodes.data() + rob_bone_data->nodes.size(); - for (bone_node* i = i_begin; i != i_end; i++) - i->parent = &rob_bone_data->nodes[*parent_indices++]; + for (bone_node* i = i_begin_1; i != i_end; i++) + i->parent = &i_begin[*parent_indices++]; } static void rob_chara_bone_data_set_rot_y(rob_chara_bone_data* rob_bone_data, float_t rot_y) { rob_bone_data->motion_loaded.front()->bone_data.rot_y = rot_y; } +static void rob_chara_bone_data_set_step(rob_chara_bone_data* rob_bone_data, float_t step) { + for (motion_blend_mot*& i : rob_bone_data->motion_loaded) + i->set_step(step); +} + static void sub_140413EB0(struc_308* a1) { a1->field_8C = false; a1->field_4C = a1->mat; @@ -10763,636 +10670,6 @@ static void sub_140505FB0(struc_209* a1) { a1->field_1F26 = 0; } -static bool rob_chara_item_cos_data_check_for_npr_flag(rob_chara_item_cos_data* item_cos_data) { - int32_t* arr = item_cos_data->cos.arr; - for (int32_t i = ITEM_SUB_ZUJO; i < ITEM_SUB_MAX; i++) { - item_table_item* item = item_table_array_get_item(item_cos_data->chara_index, *arr++); - if (item && item->npr_flag) - return true; - } - return false; -} - -static object_info rob_chara_item_cos_data_get_head_object_replace( - rob_chara_item_cos_data* item_cos_data, int32_t head_object_id) { - auto elem = item_cos_data->head_replace.find(head_object_id); - if (elem != item_cos_data->head_replace.end()) - return elem->second; - return object_info(); -} - -static float_t rob_chara_item_cos_data_get_max_face_depth(rob_chara_item_cos_data* item_cos_data) { - float_t max_face_depth = 0.0f; - for (int32_t i = ITEM_SUB_ZUJO; i < ITEM_SUB_MAX; i++) { - int32_t item_no = item_cos_data->cos.arr[i]; - item_table_item* item = item_table_array_get_item(item_cos_data->chara_index, item_no); - if (item && max_face_depth < item->face_depth) - max_face_depth = item->face_depth; - } - return max_face_depth; -} - -static int32_t item_cos_tex_chg_counter = 0; - -static texture_id item_cos_tex_chg_counter_get(texture_id id) { - if (id != texture_id(0x30, 0)) - return id; - - texture_id tex_id = texture_id(0x30, item_cos_tex_chg_counter); - if (item_cos_tex_chg_counter < 0xFFFFFFF) - item_cos_tex_chg_counter++; - else - item_cos_tex_chg_counter = 0; - return tex_id; -} - -static void sub_140526FD0(rob_chara_item_cos_data* item_cos_data, int32_t item_no, item_table_item* item) { - if (!(item->attr & 0x0C)) - return; - - if (!(item->attr & 0x08)) { - std::vector tex_chg_vec; - for (item_table_item_data_tex& i : item->data.tex) { - item_cos_texture_change_tex chg_tex; - chg_tex.org = texture_storage_get_texture(i.org); - chg_tex.chg = texture_storage_get_texture(i.chg); - chg_tex.changed = false; - tex_chg_vec.push_back(chg_tex); - } - item_cos_data->texture_change.insert({ item_no, tex_chg_vec }); - return; - } - else if (item->data.col.size() <= 0) - return; - - std::vector chg_tex_ids; - if (item->attr & 0x04) - for (item_table_item_data_tex& i : item->data.tex) - chg_tex_ids.push_back(i.chg); - else - for (item_table_item_data_col& i : item->data.col) - chg_tex_ids.push_back(i.tex_id); - - std::vector tex_chg_vec; - size_t index = 0; - for (int32_t& i : chg_tex_ids) { - size_t j = &i - chg_tex_ids.data(); - texture* tex = texture_storage_get_texture(i); - if (!tex) { - index++; - continue; - } - - bool changed = false; - if (item->data.col[j].flag & 0x01) { - tex = texture_copy(item_cos_tex_chg_counter_get(texture_id(0x30, 0)), tex); - texture_apply_color_tone(tex, tex, &item->data.col[j].col_tone); - changed = true; - } - - item_cos_texture_change_tex chg_tex; - chg_tex.org = texture_storage_get_texture(item->data.col[j].tex_id); - chg_tex.chg = tex; - chg_tex.changed = changed; - tex_chg_vec.push_back(chg_tex); - } - item_cos_data->texture_change.insert({ item_no, tex_chg_vec }); -} - -static void sub_140527280(rob_chara_item_cos_data* item_cos_data, int32_t item_no, item_table_item* item) { - if (!(item->attr & 0x20) || !(item->attr & 0xC)) - return; - - for (item_table_item_data_obj& i : item->data.obj) { - if (i.rpk == ITEM_NONE) - continue; - - auto elem = item_cos_data->item_change.find(i.rpk); - if (elem == item_cos_data->item_change.end()) { - std::vector vec; - vec.push_back(item_no); - item_cos_data->item_change.insert({ i.rpk, vec }); - } - else { - std::vector vec = elem->second; - vec.push_back(item_no); - item_cos_data->item_change.insert_or_assign(i.rpk, vec); - } - } -} - -static void sub_140522990(rob_chara_item_cos_data* item_cos_data) { - rob_chara_item_cos_data_texture_change_clear(item_cos_data); - rob_chara_item_cos_data_texture_pattern_clear(item_cos_data); - for (int32_t sub_id = ITEM_SUB_ZUJO; sub_id < ITEM_SUB_MAX; sub_id++) { - int32_t item_no = item_cos_data->cos.arr[sub_id]; - if (!item_no || sub_id == ITEM_SUB_KUTSU) - continue; - - item_table_item* item = item_table_array_get_item(item_cos_data->chara_index, item_no); - if (!item) - continue; - - sub_140526FD0(item_cos_data, item_no, item); - sub_140527280(item_cos_data, item_no, item); - } -} - -static void sub_14052B4C0(rob_chara_item_cos_data* item_cos_data, rob_chara_item_equip* rob_itm_equip, item_id id, bool object) { - if (object) - rob_chara_item_equip_set_texture_pattern(rob_itm_equip, 0, 0); - else - rob_chara_item_equip_set_object_texture_pattern(rob_itm_equip, id, 0, 0); -} - -static void sub_14052CCC0(rob_chara_item_cos_data* item_cos_data, rob_chara_item_equip* rob_itm_equip, - std::vector& item_nos, item_id id, bool a5) { - if (id < ITEM_BODY || id >= ITEM_MAX) - return; - - for (uint32_t& i : item_nos) { - auto elem = item_cos_data->texture_change.find(i); - if (elem == item_cos_data->texture_change.end()) - continue; - - for (item_cos_texture_change_tex& j : elem->second) - if (j.org && j.chg) - item_cos_data->texture_pattern[id].push_back({ j.org->id, j.chg->id }); - } - - if (a5) - rob_chara_item_equip_set_texture_pattern(rob_itm_equip, - item_cos_data->texture_pattern[id].data(), item_cos_data->texture_pattern[id].size()); - else - rob_chara_item_equip_set_object_texture_pattern(rob_itm_equip, id, - item_cos_data->texture_pattern[id].data(), item_cos_data->texture_pattern[id].size()); -} - -static void sub_140513C40(rob_chara_item_equip* rob_itm_equip, item_id id, object_info obj_info, - bone_database* bone_data, void* data, object_database* obj_db) { - if ((uint32_t)(id - 15) > 0xF) - obj_info = object_info(); - rob_chara_item_equip_load_object_info(rob_itm_equip, obj_info, id, 0, bone_data, data, obj_db); -} - -static void sub_140522A30(rob_chara_item_cos_data* item_cos_data, rob_chara_item_equip* rob_itm_equip, - bone_database* bone_data, void* data, object_database* obj_db) { - const chara_init_data* chr_init_data = chara_init_data_get(item_cos_data->chara_index); - for (int32_t i = ITEM_ATAMA; i < ITEM_KAMI; i++) { - sub_140513950(rob_itm_equip, (item_id)i, chr_init_data->field_7E4[i - 1], false, bone_data, data, obj_db); - auto elem = item_cos_data->item_change.find(i); - if (elem == item_cos_data->item_change.end()) - sub_14052B4C0(item_cos_data, rob_itm_equip, (item_id)i, false); - else - sub_14052CCC0(item_cos_data, rob_itm_equip, elem->second, (item_id)i, false); - } - - for (int32_t i = ITEM_KAMI; i < ITEM_MAX; i++) { - sub_140513C40(rob_itm_equip, (item_id)i, object_info(), bone_data, data, obj_db); - sub_14052B4C0(item_cos_data, rob_itm_equip, (item_id)i, false); - } - sub_14052B4C0(item_cos_data, rob_itm_equip, ITEM_NONE, true); - - item_cos_data->field_F0.clear(); - item_cos_data->field_100.clear(); - item_cos_data->head_replace.clear(); -} - -static item_id sub_140525B90(rob_chara_item_cos_data* item_cos_data, item_sub_id sub_id) { - const item_id v3[] = { - ITEM_ZUJO, ITEM_KAMI, ITEM_NONE, ITEM_NONE, - ITEM_MEGANE, ITEM_NONE, ITEM_KUCHI, ITEM_NONE, - ITEM_KUBI, ITEM_NONE, ITEM_OUTER, ITEM_NONE, - ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_NONE, - ITEM_JOHA_USHIRO, ITEM_NONE, ITEM_NONE, ITEM_PANTS, - ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_NONE, - ITEM_NONE, - }; - - return v3[sub_id]; -} - -static void rob_chara_item_cos_data_set_texture_pattern(rob_chara_item_cos_data* item_cos_data, - rob_chara_item_equip* rob_itm_equip, uint32_t item_no, item_id id, bool tex_pat_for_all) { - if (id < ITEM_BODY || id >= ITEM_MAX) - return; - - auto elem = item_cos_data->texture_change.find(item_no); - if (elem == item_cos_data->texture_change.end()) - return; - - for (item_cos_texture_change_tex& i : elem->second) - if (i.org && i.chg) - item_cos_data->texture_pattern[id].push_back({ i.org->id, i.chg->id }); - - if (tex_pat_for_all) - rob_chara_item_equip_set_texture_pattern(rob_itm_equip, - item_cos_data->texture_pattern[id].data(), item_cos_data->texture_pattern[id].size()); - else - rob_chara_item_equip_set_object_texture_pattern(rob_itm_equip, id, - item_cos_data->texture_pattern[id].data(), item_cos_data->texture_pattern[id].size()); -} - -static item_id sub_140512EF0(rob_chara_item_equip* rob_itm_eq, item_id id) { - int32_t v2 = id; - if (id < ITEM_BODY || id >= ITEM_ITEM16) - return ITEM_NONE; - - for (rob_chara_item_equip_object* i = &rob_itm_eq->item_equip_object[id]; - i->obj_info.not_null(); i++) { - if (++v2 >= ITEM_ITEM16) - return ITEM_NONE; - } - return (item_id)v2; -} - -static void sub_140513B90(rob_chara_item_equip* rob_itm_equip, item_id id, object_info a3, object_info a4, - bone_database* bone_data, void* data, object_database* obj_db) { - if ((uint32_t)(id - 15) > 0xF) { - a4 = object_info(); - a3 = object_info(); - } - rob_itm_equip->field_D0 = a4; - rob_itm_equip->field_D4 = id; - rob_chara_item_equip_load_object_info(rob_itm_equip, a3, id, false, bone_data, data, obj_db); - if (id >= ITEM_BODY && id < ITEM_MAX) - rob_itm_equip->item_equip_object[id].can_disp = false; -} - -static void sub_14052C560(rob_chara_item_cos_data* item_sub_data, - rob_chara_item_equip* rob_itm_equip, int32_t item_no, item_table_item* item, item_sub_id sub_id, - bone_database* bone_data, void* data, object_database* obj_db) { - for (item_table_item_data_obj& i : item->data.obj) { - if (i.obj_info.is_null()) - break; - - if (item->type == 1) { - if (i.rpk != ITEM_NONE) { - sub_140513950(rob_itm_equip, i.rpk, i.obj_info, false, bone_data, data, obj_db); - if (item->attr & 0xC) - rob_chara_item_cos_data_set_texture_pattern(item_sub_data, - rob_itm_equip, item_no, i.rpk, false); - } - continue; - } - - else if (item->type) - continue; - - item_id id = sub_140525B90(item_sub_data, sub_id); - if (id != ITEM_NONE) { - sub_140513C40(rob_itm_equip, id, i.obj_info, bone_data, data, obj_db); - item_sub_data->field_F0.insert({ i.obj_info, id }); - if (item->attr & 0xC) - rob_chara_item_cos_data_set_texture_pattern(item_sub_data, - rob_itm_equip, item_no, id, false); - } - else { - id = sub_140512EF0(rob_itm_equip, ITEM_ITEM09); - if (id != ITEM_NONE && ((item->attr & 0x10) == 0 || &i - item->data.obj.data())) { - bool v8 = false; - if (item->attr & 0x10 && &i - item->data.obj.data() == 1) { - sub_140513B90(rob_itm_equip, id, item->data.obj[1].obj_info, - item->data.obj[0].obj_info, bone_data, data, obj_db); - v8 = true; - } - else - sub_140513C40(rob_itm_equip, id, i.obj_info, bone_data, data, obj_db); - item_sub_data->field_F0.insert({ i.obj_info, id }); - if (item->attr & 0xC) - rob_chara_item_cos_data_set_texture_pattern(item_sub_data, - rob_itm_equip, item_no, id, v8); - } - } - } - - item_sub_data->head_replace.clear(); - if (!(item->attr & 0x800)) - return; - - const char* chara_name = chara_index_get_chara_name(item_sub_data->chara_index); - char buf[0x200]; - for (int32_t i = 0; i < 9; i++) { - sprintf_s(buf, sizeof(buf), "%sITM%03d_ATAM_HEAD_%02d_SP__DIVSKN", chara_name, item_no, i); - object_info obj_info = obj_db->get_object_info(buf); - if (obj_info.not_null()) - item_sub_data->head_replace.insert({ i, obj_info }); - } -} - -static void sub_140522C60(rob_chara_item_cos_data* item_cos_data, rob_chara_item_equip* rob_itm_equip, - bone_database* bone_data, void* data, object_database* obj_db) { - if (!item_table_array_get_table(item_cos_data->chara_index)) - return; - - const item_sub_id dword_140A2BB70[] = { - ITEM_SUB_ZUJO, ITEM_SUB_KAMI, ITEM_SUB_HITAI, ITEM_SUB_ME, ITEM_SUB_MEGANE, - ITEM_SUB_MIMI, ITEM_SUB_KUCHI, ITEM_SUB_MAKI, ITEM_SUB_KUBI, ITEM_SUB_INNER, - ITEM_SUB_JOHA_MAE, ITEM_SUB_JOHA_USHIRO, ITEM_SUB_HADA, ITEM_SUB_U_UDE, ITEM_SUB_L_UDE, - ITEM_SUB_TE, ITEM_SUB_BELT, ITEM_SUB_ASI, ITEM_SUB_SUNE, ITEM_SUB_HEAD, ITEM_SUB_MAX, - }; - - const item_sub_id* v6 = dword_140A2BB70; - while (*v6 != ITEM_SUB_MAX) { - int32_t item_no = item_cos_data->cos.arr[*v6]; - if (item_no) { - item_table_item* item = item_table_array_get_item(item_cos_data->chara_index, item_no); - if (item && item->type != 3 && item->attr & 0x01) - sub_14052C560(item_cos_data, rob_itm_equip, item_no, item, *v6, bone_data, data, obj_db); - } - v6++; - } -} - -static void sub_14052C8C0(rob_chara_item_cos_data* item_cos_data, rob_chara_item_equip* rob_itm_equip, - int32_t item_no, item_table_item* item, bone_database* bone_data, void* data, object_database* obj_db) { - for (item_table_item_data_obj& i : item->data.obj) { - object_info v11 = i.obj_info; - item_id id = i.rpk; - if (item->type == 1) { - if (v11.is_null()) { - if (id == ITEM_NONE) - continue; - } - else if (id == ITEM_NONE) { - } - else if (i.rpk < 100 || i.rpk > 105) { - sub_140513950(rob_itm_equip, id, v11, false, bone_data, data, obj_db); - } - else { - item_id v13 = sub_140512EF0(rob_itm_equip, ITEM_KAMI); - if (v13 == ITEM_NONE) - continue; - - sub_140513C40(rob_itm_equip, v13, i.obj_info, bone_data, data, obj_db); - item_cos_data->field_F0.insert({ i.obj_info, v13 }); - item_cos_data->field_100.insert({ id, v13 }); - id = v13; - } - } - else if (item->type == 0) { - id = sub_140512EF0(rob_itm_equip, ITEM_ITEM09); - if (id != ITEM_NONE) { - sub_140513C40(rob_itm_equip, id, i.obj_info, bone_data, data, obj_db); - item_cos_data->field_F0.insert({ i.obj_info, id }); - continue; - } - } - else - continue; - - if (item->attr & 0xC) - rob_chara_item_cos_data_set_texture_pattern(item_cos_data, rob_itm_equip, item_no, id, false); - } -} - -static void sub_140522D00(rob_chara_item_cos_data* item_cos_data, rob_chara_item_equip* rob_itm_equip, - bone_database* bone_data, void* data, object_database* obj_db) { - if (!item_table_array_get_table(item_cos_data->chara_index)) - return; - - const item_sub_id dword_140A2BBC8[] = { - ITEM_SUB_KATA, ITEM_SUB_PANTS, ITEM_SUB_MAX, - }; - - const item_sub_id* v6 = dword_140A2BBC8; - while (*v6 != ITEM_SUB_MAX) { - int32_t item_no = item_cos_data->cos.arr[*v6]; - if (item_no) { - item_table_item* item = item_table_array_get_item(item_cos_data->chara_index, item_no); - if (item && item->type != 3 && item->attr & 0x01) - sub_14052C8C0(item_cos_data, rob_itm_equip, item_no, item, bone_data, data, obj_db); - } - v6++; - } -} - -static void sub_140525D90(rob_chara_item_cos_data* item_cos_data, rob_chara_item_equip* rob_itm_equip, - int32_t a3, bone_database* bone_data, void* data, object_database* obj_db) { - auto elem = item_cos_data->field_100.find(a3); - if (elem != item_cos_data->field_100.end()) - sub_140513C40(rob_itm_equip, (item_id)elem->second, object_info(), bone_data, data, obj_db); -} - -static void sub_140522D90(rob_chara_item_cos_data* item_cos_data, rob_chara_item_equip* rob_itm_equip, - bone_database* bone_data, void* data, object_database* obj_db) { - if (!item_table_array_get_table(item_cos_data->chara_index)) - return; - - const item_sub_id dword_140A2BBD8[] = { - ITEM_SUB_COSI, ITEM_SUB_OUTER, ITEM_SUB_MAX, - }; - - const item_sub_id* v6 = dword_140A2BBD8; - while (*v6 != ITEM_SUB_MAX) { - int32_t item_no = item_cos_data->cos.arr[*v6]; - if (!item_no) { - v6++; - continue; - } - - item_table_item* item = item_table_array_get_item(item_cos_data->chara_index, item_no); - if (!item || item->type != 1) - continue; - - for (item_table_item_data_obj& i : item->data.obj) { - if (i.obj_info.is_null()) - continue; - - item_id id = sub_140525B90(item_cos_data, *v6); - sub_140513C40(rob_itm_equip, id, i.obj_info, bone_data, data, obj_db); - item_cos_data->field_F0.insert({ i.obj_info, id }); - if (item->attr & 0xC) - rob_chara_item_cos_data_set_texture_pattern(item_cos_data, rob_itm_equip, item_no, id, false); - - if (i.rpk == ITEM_NONE) - continue; - - sub_140513950(rob_itm_equip, i.rpk, object_info(), false, bone_data, data, obj_db); - switch (i.rpk) { - case ITEM_UDE_R: - sub_140525D90(item_cos_data, rob_itm_equip, 102, bone_data, data, obj_db); - break; - case ITEM_UDE_L: - sub_140525D90(item_cos_data, rob_itm_equip, 100, bone_data, data, obj_db); - break; - case ITEM_TE_R: - sub_140525D90(item_cos_data, rob_itm_equip, 103, bone_data, data, obj_db); - break; - case ITEM_TE_L: - sub_140525D90(item_cos_data, rob_itm_equip, 101, bone_data, data, obj_db); - break; - case ITEM_SUNE: - sub_140525D90(item_cos_data, rob_itm_equip, 104, bone_data, data, obj_db); - break; - case ITEM_ASI: - sub_140525D90(item_cos_data, rob_itm_equip, 105, bone_data, data, obj_db); - break; - } - } - v6++; - } -} - -static void sub_140522F90(rob_chara_item_cos_data* item_cos_data, rob_chara_item_equip* rob_itm_equip) { - if (!item_table_array_get_table(item_cos_data->chara_index)) - return; - - int32_t item_no = item_cos_data->cos.arr[ITEM_SUB_KUTSU]; - if (!item_no) - return; - - item_table_item* item = item_table_array_get_item(item_cos_data->chara_index, item_no); - if (!item || item->data.col.size()) - return; - - vec3 texture_color_coeff = item->data.col[0].col_tone.blend; - vec3 texture_color_offset = item->data.col[0].col_tone.offset; - vec3 texture_specular_coeff = 1.0f; - vec3 texture_specular_offset = 0.0f; - if (item->data.col.size() > 1) { - texture_specular_coeff = item->data.col[1].col_tone.blend; - texture_specular_offset = item->data.col[1].col_tone.offset; - } - - for (int32_t i = ITEM_ATAMA; i <= ITEM_ITEM16; i++) { - if (rob_chara_item_equip_get_object_info(rob_itm_equip, (item_id)i).is_null()) - continue; - - rob_chara_item_equip_object* itm_eq_obj = &rob_itm_equip->item_equip_object[i]; - itm_eq_obj->texture_data.field_0 = false; - itm_eq_obj->texture_data.texture_color_coeff = texture_color_coeff; - itm_eq_obj->texture_data.texture_color_offset = texture_color_offset; - itm_eq_obj->texture_data.texture_specular_coeff = texture_specular_coeff; - itm_eq_obj->texture_data.texture_specular_offset = texture_specular_offset; - } -} - -static void sub_140523230(rob_chara_item_cos_data* item_cos_data, - rob_chara_item_equip* rob_itm_equip, int32_t item_no) { - if (!item_no) - return; - - item_table_item* item = item_table_array_get_item(item_cos_data->chara_index, item_no); - if (!item || !((item->type + 1) & ~0x04)) - return; - - if (item->attr & 0x02) - for (item_table_item_data_ofs& i : item->data.ofs) { - if (item_cos_data->cos.arr[i.sub_id] != i.no) - continue; - - for (item_table_item_data_obj& j : item->data.obj) { - if (j.obj_info.is_null()) - break; - - auto elem = item_cos_data->field_F0.find(j.obj_info); - if (elem != item_cos_data->field_F0.end()) - rob_chara_item_equip_set_null_blocks_expression_data( - rob_itm_equip, elem->second, &i.position, &i.rotation, &i.scale); - } - break; - } - - for (item_table_item_data_obj& i : item->data.obj) { - if (i.obj_info.is_null()) - break; - - auto elem = item_cos_data->field_F0.find(i.obj_info); - if (elem != item_cos_data->field_F0.end()) { - item_id id = elem->second; - sub_140514130(rob_itm_equip, id, !(item->attr & 0x80)); - sub_140514110(rob_itm_equip, id, !(item->attr & 0x100)); - sub_140513C60(rob_itm_equip, id, !(item->attr & 0x400)); - } - } -} - -static void sub_1405231D0(rob_chara_item_cos_data* item_cos_data, rob_chara_item_equip* rob_itm_equip) { - if (!item_table_array_get_table(item_cos_data->chara_index)) - return; - - for (int32_t i = ITEM_SUB_ZUJO; i < ITEM_SUB_MAX; i++) - sub_140523230(item_cos_data, rob_itm_equip, item_cos_data->cos.arr[i]); -} - -static void sub_1405234E0(rob_chara_item_cos_data* item_cos_data) { - item_cos_data->item_change.clear(); - item_cos_data->field_F0.clear(); - item_cos_data->field_100.clear(); -} - -static void rob_chara_item_cos_data_reload_items(rob_chara_item_cos_data* item_cos_data, - int32_t chara_id, bone_database* bone_data, void* data, object_database* obj_db) { - rob_chara_item_equip* rob_itm_equip = rob_chara_array_get_item_equip(chara_id); - sub_140522990(item_cos_data); - sub_140522A30(item_cos_data, rob_itm_equip, bone_data, data, obj_db); - sub_140522C60(item_cos_data, rob_itm_equip, bone_data, data, obj_db); - sub_140522D00(item_cos_data, rob_itm_equip, bone_data, data, obj_db); - sub_140522D90(item_cos_data, rob_itm_equip, bone_data, data, obj_db); - sub_140522F90(item_cos_data, rob_itm_equip); - sub_1405231D0(item_cos_data, rob_itm_equip); - sub_1405234E0(item_cos_data); -} - -static void rob_chara_item_cos_data_set_chara_index( - rob_chara_item_cos_data* item_cos_data, chara_index chara_index) { - item_cos_data->chara_index = chara_index; -} - -static void rob_chara_item_cos_data_set_chara_index_item_nos( - rob_chara_item_cos_data* item_cos_data, chara_index chara_index, int32_t* items) { - rob_chara_item_cos_data_set_chara_index(item_cos_data, chara_index); - rob_chara_item_cos_data_set_item_nos(item_cos_data, items); -} - -static void rob_chara_item_cos_data_set_item(rob_chara_item_cos_data* item_cos_data, int32_t item_no) { - if (!item_no) - return; - - item_table_item* item = item_table_array_get_item(item_cos_data->chara_index, item_no); - if (item) - item_cos_data->cos.arr[item->sub_id] = item_no; -} - -static void rob_chara_item_cos_data_set_item_array(rob_chara_item_cos_data* item_cos_data, - rob_chara_pv_data_item* item) { - for (int32_t i = 0; i < 4; i++) - rob_chara_item_cos_data_set_item(item_cos_data, item->arr[i]); -} - -static void rob_chara_item_cos_data_set_item_no( - rob_chara_item_cos_data* item_cos_data, item_sub_id sub_id, int32_t item_no) { - item_cos_data->cos.arr[sub_id] = item_no; -} - -static void rob_chara_item_cos_data_set_item_nos( - rob_chara_item_cos_data* item_cos_data, int32_t* item_nos) { - for (int32_t i = ITEM_SUB_ZUJO; i < ITEM_SUB_MAX; i++) - rob_chara_item_cos_data_set_item_no(item_cos_data, (item_sub_id)i, item_nos[i]); -} - -static void rob_chara_item_cos_data_set_item_zero(rob_chara_item_cos_data* item_cos_data, int32_t item_no) { - item_table_item* item = item_table_array_get_item(item_cos_data->chara_index, item_no); - if (item) - item_cos_data->cos.arr[item->sub_id] = 0; -} - -static void rob_chara_item_cos_data_texture_change_clear(rob_chara_item_cos_data* item_cos_data) { - for (auto i : item_cos_data->texture_change) - for (item_cos_texture_change_tex& j : i.second) - if (j.changed) { - texture_free(j.chg); - j.chg = 0; - } - item_cos_data->texture_change.clear(); -} - -static void rob_chara_item_cos_data_texture_pattern_clear(rob_chara_item_cos_data* item_cos_data) { - for (std::vector& i : item_cos_data->texture_pattern) - i.clear(); -} - static float_t sub_140512F60(rob_chara_item_equip* rob_itm_equip) { float_t v26; float_t v27; @@ -11454,327 +10731,10 @@ static float_t sub_140512F60(rob_chara_item_equip* rob_itm_equip) { return *v22; } -static void sub_140512C20(rob_chara_item_equip* rob_itm_equip, render_context* rctx) { - if (rob_itm_equip->field_D0.is_null() || rob_itm_equip->field_D4 == ITEM_NONE) - return; - - mat4 mat = mat4_identity; - const char* name; - if (rob_itm_equip->field_DC == 1) { - name = "kl_te_r_wj"; - mat4_translate(0.0f, 0.0f, 0.082f, &mat); - mat4_rotate_mult(&mat, (float_t)(-90.9 * DEG_TO_RAD), 0.0f, (float_t)(-179.5 * DEG_TO_RAD), &mat); - } - if (rob_itm_equip->field_DC == 2) { - name = "kl_te_l_wj"; - mat4_translate(0.0f, 0.0015f, -0.0812f, &mat); - mat4_rotate_mult(&mat, (float_t)(-34.5 * DEG_TO_RAD), 0.0f, (float_t)(-179.5 * DEG_TO_RAD), &mat); - } - else - name = "j_1_hyoutan_000wj"; - - data_struct* aft_data = &data_list[DATA_AFT]; - bone_database* aft_bone_data = &aft_data->data_ft.bone_data; - - bone_node* node = rob_itm_equip->item_equip_object[rob_itm_equip->field_D4].get_bone_node(name, aft_bone_data); - if (!node || !node->mat) - return; - - mat4_mult(&mat, node->mat, &mat); - int32_t tex_pat_count = (int32_t)rob_itm_equip->texture_pattern.size(); - if (tex_pat_count) - rctx->object_data.set_texture_pattern(tex_pat_count, rob_itm_equip->texture_pattern.data()); - draw_task_add_draw_object_by_object_info(rctx, 0, rob_itm_equip->field_D0, 0, 0, 0, 0, 0, false); - if (tex_pat_count) - rctx->object_data.set_texture_pattern(); -} - -static void rob_chara_item_equip_add_motion_reset_data(rob_chara_item_equip* rob_itm_equip, - int32_t motion_id, float_t frame, int32_t iterations) { - for (int32_t i = rob_itm_equip->first_item_equip_object; i < rob_itm_equip->max_item_equip_object; i++) - rob_itm_equip->item_equip_object[i].add_motion_reset_data(motion_id, frame, iterations); -} - -static void rob_chara_item_equip_disp( - rob_chara_item_equip* rob_itm_equip, int32_t chara_id, render_context* rctx) { - draw_task_flags v2 = (draw_task_flags)0; - if (rctx->chara_reflect) - enum_or(v2, DRAW_TASK_CHARA_REFLECT); - if (rctx->chara_refract) - enum_or(v2, DRAW_TASK_REFRACT); - - object_data* object_data = &rctx->object_data; - shadow* shad = rctx->draw_pass.shadow_ptr; - if (rob_itm_equip->shadow_type != -1) { - if (rob_itm_equip->field_A0 & 4) { - shadow_type_enum shadow_type = rob_itm_equip->shadow_type; - vec3 pos = rob_itm_equip->position; - pos.y -= 0.2f; - shad->field_1D0[shadow_type].push_back(pos); - - float_t v9; - if (sub_140512F60(rob_itm_equip) <= -0.2f) - v9 = -0.5f; - else - v9 = 0.05f; - rctx->draw_pass.shadow_ptr->field_1C0[shadow_type] = v9; - object_data->set_shadow_type(shadow_type); - enum_or(v2, DRAW_TASK_SHADOW); - } - - if (rob_itm_equip->field_A0 & 1) - enum_or(v2, DRAW_TASK_4); - } - object_data->set_draw_task_flags(v2); - object_data->set_chara_color(rob_itm_equip->chara_color); - - vec4 v23; - object_data->get_texture_color_coeff(v23); - - vec4& texture_color_coeff = rob_itm_equip->texture_color_coeff; - object_data->set_texture_color_coeff(texture_color_coeff); - object_data->set_wet_param(rob_itm_equip->wet); - //rctx->draw_pass.field_31C |= rob_itm_equip->field_F9; - sub_140512C20(rob_itm_equip, rctx); - //sub_1405421D0(chara_id, rctx->chara_reflect, rob_itm_equip->chara_color); - - mat4 mat = rob_itm_equip->mat; - if (rob_itm_equip->item_equip_range) - for (int32_t i = rob_itm_equip->first_item_equip_object; - i < rob_itm_equip->max_item_equip_object; i++) - rob_itm_equip->item_equip_object[i].disp(&mat, rctx); - else { - for (int32_t i = ITEM_ATAMA; i < ITEM_MAX; i++) { - draw_task_flags v18 = (draw_task_flags)0; - if (rob_itm_equip->field_18[i] == 0) { - if (rctx->chara_reflect) - enum_or(v18, DRAW_TASK_CHARA_REFLECT); - if (rctx->chara_refract) - enum_or(v18, DRAW_TASK_REFRACT); - } - - draw_task_flags v19 = (draw_task_flags)(DRAW_TASK_4 | DRAW_TASK_SHADOW); - if (i == 8) - v19 = (draw_task_flags)0; - - if (!(rob_itm_equip->field_A0 & 0x04)) - enum_and(v19, ~DRAW_TASK_SHADOW); - - object_data->set_draw_task_flags( (draw_task_flags)(v18 | v19 | DRAW_TASK_SSS)); - rob_itm_equip->item_equip_object[i].disp(&mat, rctx); - } - } - object_data->set_texture_color_coeff(v23); - object_data->set_wet_param(); - object_data->set_chara_color(); - object_data->set_draw_task_flags(); - object_data->set_shadow_type(); -} - -static object_info rob_chara_item_equip_get_object_info( - rob_chara_item_equip* rob_itm_equip, item_id id) { - if (id >= ITEM_BODY && id <= ITEM_ITEM16) - return rob_itm_equip->item_equip_object[id].obj_info; - return object_info(); -} - -static void rob_chara_item_equip_get_parent_bone_nodes( - rob_chara_item_equip* rob_itm_equip, bone_node* bone_nodes, bone_database* bone_data) { - rob_itm_equip->bone_nodes = bone_nodes; - rob_itm_equip->matrices = bone_nodes->mat; - for (int32_t i = rob_itm_equip->first_item_equip_object; - i < rob_itm_equip->max_item_equip_object; i++) - rob_itm_equip->item_equip_object[i].get_parent_bone_nodes(rob_itm_equip->bone_nodes, bone_data); -} - -static void rob_chara_item_equip_load_object_info( - rob_chara_item_equip* rob_itm_equip, object_info object_info, item_id id, - bool osage_reset, bone_database* bone_data, void* data, object_database* obj_db) { - if (id < ITEM_BODY || id > ITEM_ITEM16 || !rob_itm_equip->bone_nodes) - return; - - rob_itm_equip->item_equip_object[id].load_object_info_ex_data(object_info, - rob_itm_equip->bone_nodes, osage_reset, bone_data, data, obj_db); - rob_itm_equip->item_equip_object[id].can_disp = true; -} - -static bool sub_14053E690(RobOsageNodeDataNormalRef* normal_ref) { - normal_ref->field_0 = false; - if (normal_ref->n == 0) - return normal_ref->field_0; - - if (normal_ref->u && !memcmp(&normal_ref->u->mat, &mat4_null, sizeof(mat4))) - normal_ref->u = 0; - - if (normal_ref->d && !memcmp(&normal_ref->d->mat, &mat4_null, sizeof(mat4))) - normal_ref->d = 0; - - if (normal_ref->l && !memcmp(&normal_ref->l->mat, &mat4_null, sizeof(mat4))) - normal_ref->l = 0; - - if (normal_ref->r && !memcmp(&normal_ref->r->mat, &mat4_null, sizeof(mat4))) - normal_ref->r = 0; - - if ((normal_ref->u || normal_ref->d) && (normal_ref->l || normal_ref->r)) { - if (!normal_ref->u) - normal_ref->u = normal_ref->n; - if (!normal_ref->d) - normal_ref->d = normal_ref->n; - if (!normal_ref->l) - normal_ref->l = normal_ref->n; - if (!normal_ref->r) - normal_ref->r = normal_ref->n; - normal_ref->field_0 = true; - } - return normal_ref->field_0; -} - -static void rob_chara_item_equip_reset_init_data(rob_chara_item_equip* rob_itm_equip, - bone_node* bone_nodes) { - rob_itm_equip->reset(); - rob_itm_equip->bone_nodes = bone_nodes; - rob_itm_equip->matrices = bone_nodes->mat; - - mat4* v7 = rob_itm_equip->field_13C; - int32_t* v8 = rob_itm_equip->field_18; - for (int32_t v6 = ITEM_BODY; v6 < ITEM_MAX; v6++) { - rob_itm_equip->item_equip_object[v6].init_members(v6); - *v8++ = 0; - *v7++ = mat4_identity; - } - - rob_itm_equip->step = 1.0f; - rob_itm_equip->wet = 0.0f; - rob_itm_equip->wind_strength = 1.0f; - rob_itm_equip->chara_color = true; - rob_itm_equip->npr_flag = false; - rob_itm_equip->mat = mat4_identity; -} - -static void rob_chara_item_equip_set_alpha_draw_task_flags( - rob_chara_item_equip* rob_itm_equip, float_t alpha, draw_task_flags flags) { - if (rob_itm_equip->item_equip_range) - for (int32_t i = rob_itm_equip->first_item_equip_object; - i < rob_itm_equip->max_item_equip_object; i++) - rob_itm_equip->item_equip_object[i].set_alpha_draw_task_flags(alpha, flags); - else - for (int32_t i = ITEM_ATAMA; i < ITEM_MAX; i++) - rob_itm_equip->item_equip_object[i].set_alpha_draw_task_flags(alpha, flags); -} - -static void rob_chara_item_equip_set_item_equip_range(rob_chara_item_equip* rob_itm_equip, bool value) { - rob_itm_equip->item_equip_range = value; - if (value) { - rob_itm_equip->first_item_equip_object = ITEM_BODY; - rob_itm_equip->max_item_equip_object = ITEM_ATAMA; - } - else { - rob_itm_equip->first_item_equip_object = ITEM_ATAMA; - rob_itm_equip->max_item_equip_object = ITEM_ITEM16; - } -} - -static void rob_chara_item_equip_set_motion_reset_data( - rob_chara_item_equip* rob_itm_equip, int32_t motion_id, float_t frame) { - for (int32_t i = rob_itm_equip->first_item_equip_object; i < rob_itm_equip->max_item_equip_object; i++) - rob_itm_equip->item_equip_object[i].set_motion_reset_data(motion_id, frame); - - task_wind->ptr->reset(); -} - -static void rob_chara_item_equip_set_motion_skin_param( - rob_chara_item_equip* rob_itm_equip, int8_t chara_id, int32_t motion_id, int32_t frame) { - for (int32_t i = ITEM_KAMI; i < ITEM_ITEM16; i++) - rob_itm_equip->item_equip_object[i].set_motion_skin_param(chara_id, motion_id, frame); -} - -static void rob_chara_item_equip_set_null_blocks_expression_data( - rob_chara_item_equip* rob_itm_equip, item_id id, vec3* position, vec3* rotation, vec3* scale) { - rob_itm_equip->item_equip_object[id].set_null_blocks_expression_data( - position, rotation, scale); -} - -static void rob_chara_item_equip_set_object_texture_pattern(rob_chara_item_equip* rob_itm_equip, - item_id id, texture_pattern_struct* tex_pat, size_t count) { - rob_itm_equip->item_equip_object[id].set_texture_pattern(tex_pat, count); -} - -static void rob_chara_item_equip_set_opd_blend_data( - rob_chara_item_equip* rob_itm_equip, std::list* a2) { - rob_itm_equip->opd_blend_data.clear(); - if (!a2) - return; - - for (motion_blend_mot*& i : *a2) { - opd_blend_data v11; - v11.motion_id = i->mot_key_data.motion_id; - v11.frame = i->mot_play_data.frame_data.frame; - v11.frame_count = (float_t)i->mot_key_data.mot.frame_count - 1.0f; - v11.field_C = false; - v11.type = MOTION_BLEND_NONE; - v11.blend = 0.0f; - - if (sub_140413810(i)) { - v11.type = i->get_type(); - v11.field_C = true; - v11.blend = i->blend->blend; - } - rob_itm_equip->opd_blend_data.push_back(v11); - } - - if (rob_itm_equip->opd_blend_data.size()) { - opd_blend_data* v9 = &rob_itm_equip->opd_blend_data.back(); - if (v9->field_C && v9->type != MOTION_BLEND_FREEZE) - v9->field_C = false; - } - - if (rob_itm_equip->use_opd) - for (int32_t i = ITEM_KAMI; i < ITEM_MAX; i++) - rob_itm_equip->item_equip_object[i].check_no_opd(rob_itm_equip->opd_blend_data); -} - -static void rob_chara_item_equip_set_osage_reset(rob_chara_item_equip* rob_itm_equip) { - for (int32_t i = rob_itm_equip->first_item_equip_object; i < rob_itm_equip->max_item_equip_object; i++) - rob_itm_equip->item_equip_object[i].set_osage_reset(); - - task_wind->ptr->reset(); -} - -static void rob_chara_item_equip_set_osage_move_cancel(rob_chara_item_equip* rob_itm_equip, - uint8_t id, float_t value) { - if (id == 0) - for (int32_t i = rob_itm_equip->first_item_equip_object; i < rob_itm_equip->max_item_equip_object; i++) - rob_itm_equip->item_equip_object[i].set_osage_move_cancel(value); - else if (id == 1) - rob_itm_equip->item_equip_object[ITEM_KAMI].set_osage_move_cancel(value); - else if (id == 2) - rob_itm_equip->item_equip_object[ITEM_OUTER].set_osage_move_cancel(value); -} - -static void rob_chara_item_equip_set_osage_step(rob_chara_item_equip* rob_itm_equip, float_t value) { - rob_itm_equip->step = value; -} - -static void rob_chara_item_equip_set_shadow_type(rob_chara_item_equip* rob_itm_equip, int32_t chara_id) { - if (chara_id == 0) - rob_itm_equip->shadow_type = SHADOW_CHARA; - else - rob_itm_equip->shadow_type = SHADOW_STAGE; -} - -static void rob_chara_item_equip_set_texture_pattern(rob_chara_item_equip* rob_itm_equip, - texture_pattern_struct* tex_pat, size_t count) { - rob_itm_equip->texture_pattern.clear(); - if (count && tex_pat) - for (size_t i = 0; i < count; i++) - rob_itm_equip->texture_pattern.push_back(tex_pat[i]); -} - static void rob_chara_add_motion_reset_data(rob_chara* rob_chr, int32_t motion_id, float_t frame, int32_t iterations) { if (motion_id != -1) - rob_chara_item_equip_add_motion_reset_data(rob_chr->item_equip, - motion_id, frame, iterations); + rob_chr->item_equip->add_motion_reset_data(motion_id, frame, iterations); } static bool rob_chara_check_for_ageageagain_module(chara_index chara_index, int32_t module_index) { @@ -11784,15 +10744,14 @@ static bool rob_chara_check_for_ageageagain_module(chara_index chara_index, int3 static object_info rob_chara_get_head_object(rob_chara* rob_chr, int32_t head_object_id) { if (head_object_id < 0 || head_object_id > 8) return {}; - object_info obj_info = rob_chara_item_cos_data_get_head_object_replace( - &rob_chr->item_cos_data, head_object_id); + object_info obj_info = rob_chr->item_cos_data.get_head_object_replace(head_object_id); if (obj_info.not_null()) return obj_info; return rob_chr->chara_init_data->head_objects[head_object_id]; } static object_info rob_chara_get_object_info(rob_chara* rob_chr, item_id id) { - return rob_chara_item_equip_get_object_info(rob_chr->item_equip, id); + return rob_chr->item_equip->get_object_info(id); } static void rob_chara_load_default_motion(rob_chara* rob_chr, bone_database* bone_data, motion_database* mot_db) { @@ -11906,11 +10865,12 @@ static void rob_chara_set_pv_data(rob_chara* rob_chr, int8_t chara_id, rob_chr->pv_data = *pv_data; rob_chr->chara_init_data = chara_init_data_get(chara_index); if (!check_module_index_is_501(module_index)) { - item_cos_data* cos_data = item_table_array_get_item_cos_data_by_module_index(chara_index, module_index); - if (cos_data) - rob_chara_item_cos_data_set_chara_index_item_nos(&rob_chr->item_cos_data, chara_index, cos_data->arr); + const item_cos_data* cos + = item_table_handler_array_get_item_cos_data_by_module_index(chara_index, module_index); + if (cos) + rob_chr->item_cos_data.set_chara_index_item_nos(chara_index, cos->arr); } - rob_chara_item_cos_data_set_item_array(&rob_chr->item_cos_data, &pv_data->item); + rob_chr->item_cos_data.set_item_array(&pv_data->item); } static void rob_cmn_mottbl_read(void* a1, const void* data, size_t size) { @@ -11959,16 +10919,6 @@ static rob_manager_rob_impl* rob_manager_rob_impls2_get(TaskRobManager* rob_mgr) return rob_manager_rob_impls2; } -static void skin_param_manager_reset(int32_t chara_id) { - skin_param_manager[chara_id].Reset(); -} - -static bool task_rob_load_check_load_req_data() { - if (task_rob_load->field_F0 == 2 && !task_rob_load->load_req_data_obj.size()) - return !!task_rob_load->load_req_data.size(); - return true; -} - bool rob_chara_array_check_visibility(int32_t chara_id) { if (rob_chara_pv_data_array_check_chara_id(chara_id)) return rob_chara_array[chara_id].is_visible(); @@ -11994,6 +10944,10 @@ float_t rob_chara_array_get_data_adjust_scale(int32_t chara_id) { return 1.0f; } +rob_chara_item_cos_data* rob_chara_array_get_item_cos_data(int32_t chara_id) { + return &rob_chara_array[chara_id].item_cos_data; +} + rob_chara_item_equip* rob_chara_array_get_item_equip(int32_t chara_id) { return rob_chara_array[chara_id].item_equip; } @@ -12027,12 +10981,19 @@ void rob_chara_array_free_chara_id(int32_t chara_id) { rob_chara_pv_data_array[chara_id].type = ROB_CHARA_TYPE_NONE; } +void rob_chara_array_reset_bone_data_item_equip(int32_t chara_id) { + rob_chara_item_equip* rob_itm_equip = rob_chara_array[chara_id].item_equip; + rob_itm_equip->reset_init_data(rob_chara_bone_data_get_node(rob_chara_array[chara_id].bone_data, 0)); + rob_itm_equip->set_shadow_type(chara_id); + rob_itm_equip->field_A0 = 5; +} + void rob_chara_array_set_alpha_draw_task_flags(int32_t chara_id, float_t alpha, draw_task_flags flags) { rob_chara* rob_chr = rob_chara_array_get(chara_id); if (!rob_chr) return; - rob_chara_item_equip_set_alpha_draw_task_flags(rob_chr->item_equip, alpha, flags); + rob_chr->item_equip->set_alpha_draw_task_flags(alpha, flags); /*if (rob_chara_check_for_ageageagain_module(rob_chr->chara_index, rob_chr->module_index)) { sub_140543120(chara_id, 1, alpha); @@ -12066,7 +11027,7 @@ int32_t rob_chara_array_reset_pv_data(int32_t chara_id) { return chara_id; } -inline bool rob_chara_pv_data_array_check_chara_id(int32_t chara_id) { +bool rob_chara_pv_data_array_check_chara_id(int32_t chara_id) { return rob_chara_pv_data_array[chara_id].type != ROB_CHARA_TYPE_NONE; } @@ -12097,8 +11058,39 @@ bool rob_chara::is_visible() { return !!(data.field_0 & 0x01); } -bool task_rob_manager_append_task() { - return app::TaskWork::AppendTask(task_rob_manager, "ROB_MANAGER TASK"); +bool task_rob_load_add_task() { + return app::TaskWork::AddTask(task_rob_load, 0, "ROB LOAD", 0); +} + +bool task_rob_load_append_free_req_data(chara_index chara_index) { + return task_rob_load->AppendFreeReqData(chara_index); +} + +bool task_rob_load_append_free_req_data_obj(chara_index chara_index, const item_cos_data* cos) { + return task_rob_load->AppendFreeReqDataObj(chara_index, cos); +} + +bool task_rob_load_append_load_req_data(chara_index chara_index) { + return task_rob_load->AppendLoadReqData(chara_index); +} + +bool task_rob_load_append_load_req_data_obj(chara_index chara_index, const item_cos_data* cos) { + return task_rob_load->AppendLoadReqDataObj(chara_index, cos); +} + +bool task_rob_load_check_load_req_data() { + if (task_rob_load->field_F0 == 2 && !task_rob_load->load_req_data_obj.size()) + return task_rob_load->load_req_data.size() != 0; + return true; +} + +bool task_rob_load_del_task() { + task_rob_load->DelTask(); + return true; +} + +bool task_rob_manager_add_task() { + return app::TaskWork::AddTask(task_rob_manager, "ROB_MANAGER TASK"); } bool task_rob_manager_check_chara_loaded(int32_t chara_id) { @@ -12111,11 +11103,19 @@ bool task_rob_manager_check_task_ready() { return app::TaskWork::CheckTaskReady(task_rob_manager); } -bool task_rob_manager_free_task() { +bool task_rob_manager_hide_task() { + return task_rob_manager->HideTask(); +} + +bool task_rob_manager_run_task() { + return task_rob_manager->RunTask(); +} + +bool task_rob_manager_del_task() { if (!app::TaskWork::CheckTaskReady(task_rob_manager)) return true; - task_rob_manager->SetDest(); + task_rob_manager->DelTask(); return false; } @@ -12370,6 +11370,17 @@ void MotionBlendCross::Blend(bone_data* a2, bone_data* a3) { mat4 v16; switch (a2->type) { + case BONE_DATABASE_BONE_ROTATION: + default: + if (a2->motion_bone_index == MOTION_BONE_KL_KOSI_Y) { + mat4_mult(&a3->rot_mat[0], &field_A4, &v16); + mat4_mult(&a2->rot_mat[0], &field_64, &v15); + mat4_blend_rotation(&v15, &v16, &v15, blend); + mat4_mult(&v15, &field_E4, &a2->rot_mat[0]); + } + else if (a2->motion_bone_index > MOTION_BONE_N_HARA) + mat4_blend_rotation(&a2->rot_mat[0], &a3->rot_mat[0], &a2->rot_mat[0], blend); + break; case BONE_DATABASE_BONE_TYPE_1: case BONE_DATABASE_BONE_POSITION: a2->trans = vec3::lerp(a3->trans, a2->trans, blend); @@ -12408,16 +11419,6 @@ void MotionBlendCross::Blend(bone_data* a2, bone_data* a3) { mat4_transpose(&a2->rot_mat[0], &v15); mat4_mult(&v16, &v15, &a2->rot_mat[1]); break; - default: - if (a2->motion_bone_index == MOTION_BONE_KL_KOSI_Y) { - mat4_mult(&a3->rot_mat[0], &field_A4, &v16); - mat4_mult(&a2->rot_mat[0], &field_64, &v15); - mat4_blend_rotation(&v15, &v16, &v15, blend); - mat4_mult(&v15, &field_E4, &a2->rot_mat[0]); - } - else if (a2->motion_bone_index > MOTION_BONE_N_HARA) - mat4_blend_rotation(&a2->rot_mat[0], &a3->rot_mat[0], &a2->rot_mat[0], blend); - break; } } @@ -12540,6 +11541,17 @@ void MotionBlendFreeze::Blend(bone_data* a2, bone_data* a3) { mat4 v15; mat4 v16; switch (a2->type) { + case BONE_DATABASE_BONE_ROTATION: + default: + if (a2->motion_bone_index == MOTION_BONE_KL_KOSI_Y) { + mat4_mult(&a2->rot_mat_prev[0][field_24], &field_B4, &v16); + mat4_mult(&a2->rot_mat[0], &field_74, &v15); + mat4_blend_rotation(&v15, &v16, &v15, blend); + mat4_mult(&v15, &field_F4, &a2->rot_mat[0]); + } + else if (a2->motion_bone_index > MOTION_BONE_N_HARA) + mat4_blend_rotation(a2->rot_mat, &a2->rot_mat_prev[0][field_24], a2->rot_mat, blend); + break; case BONE_DATABASE_BONE_TYPE_1: case BONE_DATABASE_BONE_POSITION: a2->trans = vec3::lerp(a2->trans_prev[field_24], a2->trans, blend); @@ -12578,16 +11590,6 @@ void MotionBlendFreeze::Blend(bone_data* a2, bone_data* a3) { mat4_transpose(&a2->rot_mat[0], &v15); mat4_mult(&v16, &v15, &a2->rot_mat[1]); break; - default: - if (a2->motion_bone_index == MOTION_BONE_KL_KOSI_Y) { - mat4_mult(&a2->rot_mat_prev[0][field_24], &field_B4, &v16); - mat4_mult(&a2->rot_mat[0], &field_74, &v15); - mat4_blend_rotation(&v15, &v16, &v15, blend); - mat4_mult(&v15, &field_F4, &a2->rot_mat[0]); - } - else if (a2->motion_bone_index > MOTION_BONE_N_HARA) - mat4_blend_rotation(a2->rot_mat, &a2->rot_mat_prev[0][field_24], a2->rot_mat, blend); - break; } } @@ -12629,6 +11631,10 @@ void PartialMotionBlendFreeze::Blend(bone_data* a2, bone_data* a3) { float_t inv_blend = 1.0f - blend; switch (a2->type) { + case BONE_DATABASE_BONE_ROTATION: + default: + mat4_lerp_rotation(&a2->rot_mat_prev[0][0], &a2->rot_mat[0], &a2->rot_mat[0], blend); + break; case BONE_DATABASE_BONE_POSITION: case BONE_DATABASE_BONE_TYPE_1: a2->trans = vec3::lerp(a2->trans_prev[0], a2->trans, blend); @@ -12646,13 +11652,10 @@ void PartialMotionBlendFreeze::Blend(bone_data* a2, bone_data* a3) { mat4 v17; mat4_mult(&a2->rot_mat_prev[1][0], &a2->rot_mat_prev[0][0], &v16); mat4_mult(&a2->rot_mat[1], &a2->rot_mat[0], &v15); - mat4_lerp_rotation(&v16, &v15, &v15, blend); + mat4_lerp_rotation(&v16, &v15, &v16, blend); mat4_transpose(&a2->rot_mat[0], &v17); - mat4_mult(&a2->rot_mat[1], &v17, &v15); + mat4_mult(&v17, &v16, &a2->rot_mat[1]); } break; - default: - mat4_lerp_rotation(&a2->rot_mat_prev[0][0], &a2->rot_mat[0], &a2->rot_mat[0], blend); - break; } } @@ -12712,6 +11715,11 @@ void motion_blend_mot::reset() { field_4F8.field_C8 = 0.0f; } +void motion_blend_mot::set_step(float_t step) { + mot_play_data.frame_data.step_prev = mot_play_data.frame_data.step; + mot_play_data.frame_data.step = step; +} + rob_chara_bone_data_ik_scale::rob_chara_bone_data_ik_scale() { ratio0 = 1.0f; ratio1 = 1.0f; @@ -12991,25 +11999,25 @@ void rob_chara_item_equip_object::clear_ex_data() { node_blocks.clear(); } -void rob_chara_item_equip_object::disp(mat4* mat, render_context* rctx) { +void rob_chara_item_equip_object::disp(const mat4* mat, render_context* rctx) { if (obj_info.is_null()) return; - ::draw_task_flags v2 = rctx->object_data.get_draw_task_flags(); + ::draw_task_flags v2 = rctx->disp_manager.get_draw_task_flags(); ::draw_task_flags v4 = v2; if (fabsf(alpha - 1.0f) > 0.000001f) enum_or(v4, draw_task_flags); else enum_and(v4, ~(DRAW_TASK_ALPHA_ORDER_3 | DRAW_TASK_ALPHA_ORDER_2 | DRAW_TASK_ALPHA_ORDER_1)); - rctx->object_data.set_draw_task_flags(v4); + rctx->disp_manager.set_draw_task_flags(v4); if (can_disp) { - draw_task_add_draw_object_by_object_info_object_skin(rctx, obj_info, + rctx->disp_manager.entry_obj_by_object_info_object_skin(obj_info, &texture_pattern, &texture_data, alpha, mats, ex_data_block_mats.data(), 0, mat); for (ExNodeBlock*& i : node_blocks) i->Disp(mat, rctx); } - rctx->object_data.set_draw_task_flags(v2); + rctx->disp_manager.set_draw_task_flags(v2); } int32_t rob_chara_item_equip_object::get_bone_index(const char* name, bone_database* bone_data) { @@ -13084,16 +12092,16 @@ void rob_chara_item_equip_object::init_ex_data_bone_nodes(obj_skin_ex_data* ex_d if (ex_data->bone_name_array) { ex_bones.clear(); - const char** bone_name = ex_data->bone_name_array; - for (uint32_t i = 0; i < num_bone_name; i++, bone_name++) { + const char** bone_name_array = ex_data->bone_name_array; + for (uint32_t i = 0; i < num_bone_name; i++) { bone_node* node = &v4[i]; - node->name = *bone_name; + node->name = bone_name_array[i]; node->mat = &v5[i]; *node->mat = mat4_identity; node->ex_data_mat = &v8[i]; *node->ex_data_mat = mat4_identity; - ex_bones.push_back({ *bone_name, i }); + ex_bones.push_back({ bone_name_array[i], i }); } } } @@ -13105,9 +12113,9 @@ void rob_chara_item_equip_object::init_members(size_t index) { mats = 0; texture_pattern.clear(); texture_data.field_0 = -1; - texture_data.texture_color_coeff = 1.0f; + texture_data.texture_color_coefficients = 1.0f; texture_data.texture_color_offset = 0.0f; - texture_data.texture_specular_coeff = 1.0f; + texture_data.texture_specular_coefficients = 1.0f; texture_data.texture_specular_offset = 0.0f; alpha = 1.0f; draw_task_flags = DRAW_TASK_ALPHA_ORDER_1; @@ -13121,7 +12129,7 @@ void rob_chara_item_equip_object::init_members(size_t index) { ex_data_mats.clear(); ex_data_block_mats.clear(); ex_bones.clear(); - field_1B8 = 0; + field_1B8 = false; use_opd = false; osage_nodes_count = 0; } @@ -13303,9 +12311,9 @@ void rob_chara_item_equip_object::load_ex_data(obj_skin_ex_data* ex_data, this->osage_nodes_count = osage_nodes_count; } -void rob_chara_item_equip_object::load_object_info_ex_data(object_info object_info, bone_node* bone_nodes, +void rob_chara_item_equip_object::load_object_info_ex_data(object_info obj_info, bone_node* bone_nodes, bool osage_reset, bone_database* bone_data, void* data, object_database* obj_db) { - this->obj_info = object_info; + this->obj_info = obj_info; this->bone_nodes = bone_nodes; mats = bone_nodes->mat; ex_data_bone_nodes.clear(); @@ -13328,6 +12336,13 @@ void rob_chara_item_equip_object::load_object_info_ex_data(object_info object_in osage_iterations = 60; } +void rob_chara_item_equip::load_outfit_object_info(item_id id, object_info obj_info, + bool osage_reset, bone_database* bone_data, void* data, object_database* obj_db) { + if (id <= ITEM_BODY && id >= ITEM_KAMI) + obj_info = {}; + load_object_info(obj_info, id, osage_reset, bone_data, data, obj_db); +} + void rob_chara_item_equip_object::reset_external_force() { for (ExOsageBlock*& i : osage_blocks) i->rob.ResetExtrenalForce(); @@ -13366,7 +12381,8 @@ bool rob_chara_item_equip_object::set_boc( return v6; } -void rob_chara_item_equip_object::set_collision_target_osage(skin_param_osage_root& skp_root, skin_param* skp) { +void rob_chara_item_equip_object::set_collision_target_osage( + skin_param_osage_root& skp_root, skin_param* skp) { if (!skp_root.colli_tgt_osg.size()) return; @@ -13390,28 +12406,29 @@ void rob_chara_item_equip_object::set_motion_skin_param(int8_t chara_id, int32_t if (obj_info.is_null()) return; - struc_571** v7 = 0;//sub_14060B470(chara_id, obj_info, motion_id, frame); - if (!v7) + std::vector* skp_file_data + = skin_param_manager_get_skin_param_file_data(chara_id, obj_info, motion_id, frame); + //if (!skp_file_data) return; - struc_571* v9 = *v7; - field_1B8 = 0; + field_1B8 = false; + skin_param_file_data* j = skp_file_data->data(); for (ExOsageBlock*& i : osage_blocks) { - field_1B8 |= v9->field_88; - i->SetSkinParam(v9++); + field_1B8 |= j->field_88; + i->SetSkinParam(j++); } for (ExClothBlock*& i : cloth_blocks) { - field_1B8 |= v9->field_88; - i->SetSkinParam(v9++); + field_1B8 |= j->field_88; + i->SetSkinParam(j++); } } -void rob_chara_item_equip_object::set_null_blocks_expression_data(vec3* position, vec3* rotation, vec3* scale) { - vec3 pos = *position; - vec3 rot; - vec3_mult_scalar(*rotation, DEG_TO_RAD_FLOAT, rot); - vec3 sc = *scale; +void rob_chara_item_equip_object::set_null_blocks_expression_data( + const vec3& position, const vec3& rotation, const vec3& scale) { + const vec3 pos = position; + const vec3 rot = rotation * DEG_TO_RAD_FLOAT; + const vec3 sc = scale; null_blocks_data_set = true; for (ExNullBlock*& i : null_blocks) { @@ -13460,7 +12477,7 @@ void rob_chara_item_equip_object::skp_load(void* kv, bone_database* bone_data) { if (_kv->key.size() < 1) return; - field_1B8 = 0; + field_1B8 = false; for (ExOsageBlock*& i : osage_blocks) { ExOsageBlock* osg = i; skin_param_osage_root root; @@ -13477,6 +12494,38 @@ void rob_chara_item_equip_object::skp_load(void* kv, bone_database* bone_data) { } } +void rob_chara_item_equip_object::skp_load(skin_param_osage_root& skp_root, + std::vector& vec, skin_param_file_data* skp_file_data, bone_database* bone_data) { + set_collision_target_osage(skp_root, &skp_file_data->skin_param); + skp_file_data->field_88 |= skp_file_data->skin_param.coli_type > 0; + + skin_param_osage_node* j = vec.data(); + size_t k = 0; + for (RobOsageNodeData& i : skp_file_data->nodes_data) + i.SetForce(skp_root, j++, k++); + + skp_file_data->field_88 |= skp_load_boc(skp_root, &skp_file_data->nodes_data); + skp_file_data->field_88 |= skp_load_normal_ref(skp_root, &skp_file_data->nodes_data); +} + +bool rob_chara_item_equip_object::skp_load_boc( + skin_param_osage_root& skp_root, std::vector* node_data) { + if (!skp_root.boc.size()) + return false; + + bool ret = false; + for (const skin_param_osage_root_boc& i : skp_root.boc) + for (ExOsageBlock*& j : osage_blocks) + if (!i.ed_root.compare(j->name) && i.ed_node >= 0 + && i.ed_node < node_data->size() && i.st_node >= 0 + && i.st_node < j->rob.nodes.size()) { + node_data->data()[i.ed_node].boc.push_back(j->rob.GetNode(i.st_node + 1ULL)); + ret = true; + break; + } + return ret; +} + void rob_chara_item_equip_object::skp_load_file(void* data, bone_database* bone_data, object_database* obj_db) { key_val* kv_ptr = 0;//sub_14060B4B0(itm_eq_obj->object_info); @@ -13501,7 +12550,7 @@ void rob_chara_item_equip_object::skp_load_file(void* data, bool rob_chara_item_equip_object::skp_load_normal_ref( skin_param_osage_root& skp_root, std::vector* node_data) { if (!skp_root.normal_ref.size()) - return true; + return false; for (skin_param_osage_root_normal_ref& i : skp_root.normal_ref) { size_t v12 = 0; @@ -13527,7 +12576,7 @@ bool rob_chara_item_equip_object::skp_load_normal_ref( rob_chara_item_equip::rob_chara_item_equip() : bone_nodes(), matrices(), item_equip_object(), field_18(), item_equip_range(), first_item_equip_object(), max_item_equip_object(), field_A0(), shadow_type(), position(), -field_B4(), field_D4(), disable_update(), field_DC(), texture_color_coeff(), wet(), wind_strength(), +field_B4(), field_D4(), disable_update(), field_DC(), texture_color_coefficients(), wet(), wind_strength(), chara_color(), npr_flag(), mat(), field_13C(), field_8BC(), field_8C0(), field_8C4(), field_8C8(), field_8CC(), field_8D0(), field_8D4(), field_8D8(), field_8DC(), field_8E0(), field_8E4(), field_8E8(), field_8EC(), field_8F0(), field_8F4(), field_8F8(), field_8FC(), field_900(), field_908(), field_910(), field_918(), @@ -13548,12 +12597,146 @@ rob_chara_item_equip::~rob_chara_item_equip() { } } +void rob_chara_item_equip::add_motion_reset_data(int32_t motion_id, float_t frame, int32_t iterations) { + for (int32_t i = first_item_equip_object; i < max_item_equip_object; i++) + item_equip_object[i].add_motion_reset_data(motion_id, frame, iterations); +} + +static void sub_140512C20(rob_chara_item_equip* rob_itm_equip, render_context* rctx) { + if (rob_itm_equip->field_D0.is_null() || rob_itm_equip->field_D4 == ITEM_NONE) + return; + + mat4 mat = mat4_identity; + const char* name; + if (rob_itm_equip->field_DC == 1) { + name = "kl_te_r_wj"; + mat4_translate(0.0f, 0.0f, 0.082f, &mat); + mat4_rotate_mult(&mat, (float_t)(-90.9 * DEG_TO_RAD), 0.0f, (float_t)(-179.5 * DEG_TO_RAD), &mat); + } + if (rob_itm_equip->field_DC == 2) { + name = "kl_te_l_wj"; + mat4_translate(0.0f, 0.0015f, -0.0812f, &mat); + mat4_rotate_mult(&mat, (float_t)(-34.5 * DEG_TO_RAD), 0.0f, (float_t)(-179.5 * DEG_TO_RAD), &mat); + } + else + name = "j_1_hyoutan_000wj"; + + data_struct* aft_data = &data_list[DATA_AFT]; + bone_database* aft_bone_data = &aft_data->data_ft.bone_data; + + bone_node* node = rob_itm_equip->item_equip_object[rob_itm_equip->field_D4].get_bone_node(name, aft_bone_data); + if (!node || !node->mat) + return; + + mat4_mult(&mat, node->mat, &mat); + int32_t tex_pat_count = (int32_t)rob_itm_equip->texture_pattern.size(); + if (tex_pat_count) + rctx->disp_manager.set_texture_pattern(tex_pat_count, rob_itm_equip->texture_pattern.data()); + rctx->disp_manager.entry_obj_by_object_info(0, rob_itm_equip->field_D0); + if (tex_pat_count) + rctx->disp_manager.set_texture_pattern(); +} + +void rob_chara_item_equip::disp(int32_t chara_id, render_context* rctx) { + draw_task_flags v2 = (draw_task_flags)0; + if (rctx->chara_reflect) + enum_or(v2, DRAW_TASK_CHARA_REFLECT); + if (rctx->chara_refract) + enum_or(v2, DRAW_TASK_REFRACT); + + mdl::DispManager& disp_manager = rctx->disp_manager; + shadow* shad = rctx->render_manager.shadow_ptr; + if (shadow_type != -1) { + if (field_A0 & 4) { + vec3 pos = position; + pos.y -= 0.2f; + shad->field_1D0[shadow_type].push_back(pos); + + float_t v9; + if (sub_140512F60(this) <= -0.2f) + v9 = -0.5f; + else + v9 = 0.05f; + rctx->render_manager.shadow_ptr->field_1C0[shadow_type] = v9; + disp_manager.set_shadow_type(shadow_type); + enum_or(v2, DRAW_TASK_SHADOW); + } + + if (field_A0 & 1) + enum_or(v2, DRAW_TASK_4); + } + disp_manager.set_draw_task_flags(v2); + disp_manager.set_chara_color(chara_color); + + vec4 temp_texture_color_coeff; + disp_manager.get_texture_color_coeff(temp_texture_color_coeff); + + disp_manager.set_texture_color_coefficients(texture_color_coefficients); + disp_manager.set_wet_param(wet); + //rctx->render_manager.field_31C |= field_F9; + sub_140512C20(this, rctx); + //sub_1405421D0(chara_id, rctx->chara_reflect, chara_color); + + if (item_equip_range) + for (int32_t i = first_item_equip_object; i < max_item_equip_object; i++) + item_equip_object[i].disp(&mat, rctx); + else { + for (int32_t i = ITEM_ATAMA; i < ITEM_MAX; i++) { + draw_task_flags v18 = (draw_task_flags)0; + if (field_18[i] == 0) { + if (rctx->chara_reflect) + enum_or(v18, DRAW_TASK_CHARA_REFLECT); + if (rctx->chara_refract) + enum_or(v18, DRAW_TASK_REFRACT); + } + + draw_task_flags v19 = (draw_task_flags)(DRAW_TASK_4 | DRAW_TASK_SHADOW); + if (i == 8) + v19 = (draw_task_flags)0; + + if (!(field_A0 & 0x04)) + enum_and(v19, ~DRAW_TASK_SHADOW); + + disp_manager.set_draw_task_flags((draw_task_flags)(v18 | v19 | DRAW_TASK_SSS)); + item_equip_object[i].disp(&mat, rctx); + } + } + disp_manager.set_texture_color_coefficients(temp_texture_color_coeff); + disp_manager.set_wet_param(); + disp_manager.set_chara_color(); + disp_manager.set_draw_task_flags(); + disp_manager.set_shadow_type(); +} + rob_chara_item_equip_object* rob_chara_item_equip::get_item_equip_object(item_id id) { if (id >= ITEM_BODY && id <= ITEM_ITEM16) return &item_equip_object[id]; return 0; } +object_info rob_chara_item_equip::get_object_info(item_id id) { + if (id >= ITEM_BODY && id <= ITEM_ITEM16) + return item_equip_object[id].obj_info; + return {}; +} + +void rob_chara_item_equip::get_parent_bone_nodes(bone_node* bone_nodes, bone_database* bone_data) { + this->bone_nodes = bone_nodes; + matrices = bone_nodes->mat; + for (int32_t i = first_item_equip_object; i < max_item_equip_object; i++) + item_equip_object[i].get_parent_bone_nodes(bone_nodes, bone_data); +} + +void rob_chara_item_equip::load_object_info(object_info obj_info, item_id id, + bool osage_reset, bone_database* bone_data, void* data, object_database* obj_db) { + if (id < ITEM_BODY || id > ITEM_ITEM16 || !bone_nodes) + return; + + item_equip_object[id].load_object_info_ex_data(obj_info, + bone_nodes, osage_reset, bone_data, data, obj_db); + item_equip_object[id].can_disp = true; +} + void rob_chara_item_equip::reset() { bone_nodes = 0; matrices = 0; @@ -13563,7 +12746,7 @@ void rob_chara_item_equip::reset() { shadow_type = (shadow_type_enum)-1; field_A0 = 5; texture_pattern.clear(); - field_D0 = object_info(); + field_D0 = {}; field_D4 = -1; disable_update = false; parts_short = false; @@ -13580,11 +12763,153 @@ void rob_chara_item_equip::reset_external_force() { item_equip_object[i].reset_external_force(); } +void rob_chara_item_equip::reset_init_data(bone_node* bone_nodes) { + reset(); + this->bone_nodes = bone_nodes; + matrices = bone_nodes->mat; + + mat4* v7 = field_13C; + int32_t* v8 = field_18; + for (int32_t i = ITEM_BODY; i < ITEM_MAX; i++) { + item_equip_object[i].init_members(i); + *v8++ = 0; + *v7++ = mat4_identity; + } + + step = 1.0f; + wet = 0.0f; + wind_strength = 1.0f; + chara_color = true; + npr_flag = false; + mat = mat4_identity; +} + +void rob_chara_item_equip::set_alpha_draw_task_flags(float_t alpha, draw_task_flags flags) { + if (item_equip_range) + for (int32_t i = first_item_equip_object; i < max_item_equip_object; i++) + item_equip_object[i].set_alpha_draw_task_flags(alpha, flags); + else + for (int32_t i = ITEM_ATAMA; i < ITEM_MAX; i++) + item_equip_object[i].set_alpha_draw_task_flags(alpha, flags); +} + +void rob_chara_item_equip::set_disp(item_id id, bool value) { + item_equip_object[id].can_disp = value; +} + +void rob_chara_item_equip::set_item_equip_range(bool value) { + item_equip_range = value; + if (value) { + first_item_equip_object = ITEM_BODY; + max_item_equip_object = ITEM_ATAMA; + } + else { + first_item_equip_object = ITEM_ATAMA; + max_item_equip_object = ITEM_ITEM16; + } +} + +void rob_chara_item_equip::set_motion_reset_data(int32_t motion_id, float_t frame) { + for (int32_t i = first_item_equip_object; i < max_item_equip_object; i++) + item_equip_object[i].set_motion_reset_data(motion_id, frame); + + task_wind->ptr->reset(); +} + +void rob_chara_item_equip::set_motion_skin_param(int8_t chara_id, int32_t motion_id, int32_t frame) { + for (int32_t i = ITEM_KAMI; i < ITEM_ITEM16; i++) + item_equip_object[i].set_motion_skin_param(chara_id, motion_id, frame); +} + +void rob_chara_item_equip::set_null_blocks_expression_data(item_id id, + const vec3& position, const vec3& rotation, const vec3& scale) { + item_equip_object[id].set_null_blocks_expression_data(position, rotation, scale); +} + +void rob_chara_item_equip::set_object_texture_pattern(item_id id, + texture_pattern_struct* tex_pat, size_t count) { + item_equip_object[id].set_texture_pattern(tex_pat, count); +} + +void rob_chara_item_equip::set_opd_blend_data(std::list* a2) { + opd_blend_data.clear(); + if (!a2) + return; + + for (motion_blend_mot*& i : *a2) { + ::opd_blend_data v11; + v11.motion_id = i->mot_key_data.motion_id; + v11.frame = i->mot_play_data.frame_data.frame; + v11.frame_count = (float_t)i->mot_key_data.mot.frame_count - 1.0f; + v11.field_C = false; + v11.type = MOTION_BLEND_NONE; + v11.blend = 0.0f; + + if (sub_140413810(i)) { + v11.type = i->get_type(); + v11.field_C = true; + v11.blend = i->blend->blend; + } + opd_blend_data.push_back(v11); + } + + if (opd_blend_data.size()) { + ::opd_blend_data* v9 = &opd_blend_data.back(); + if (v9->field_C && v9->type != MOTION_BLEND_FREEZE) + v9->field_C = false; + } + + if (use_opd) + for (int32_t i = ITEM_KAMI; i < ITEM_MAX; i++) + item_equip_object[i].check_no_opd(opd_blend_data); +} + +void rob_chara_item_equip::set_osage_reset() { + for (int32_t i = first_item_equip_object; i < max_item_equip_object; i++) + item_equip_object[i].set_osage_reset(); + + task_wind->ptr->reset(); +} + +void rob_chara_item_equip::set_osage_move_cancel(uint8_t id, float_t value) { + if (id == 0) + for (int32_t i = first_item_equip_object; i < max_item_equip_object; i++) + item_equip_object[i].set_osage_move_cancel(value); + else if (id == 1) + item_equip_object[ITEM_KAMI].set_osage_move_cancel(value); + else if (id == 2) + item_equip_object[ITEM_OUTER].set_osage_move_cancel(value); +} + void rob_chara_item_equip::set_osage_play_data_init(item_id id, float_t* opdi_data) { if (id >= ITEM_KAMI && id <= ITEM_ITEM16) item_equip_object[id].set_osage_play_data_init(opdi_data); } +void rob_chara_item_equip::set_shadow_type(int32_t chara_id) { + if (chara_id == 0) + shadow_type = SHADOW_CHARA; + else + shadow_type = SHADOW_STAGE; +} + +void rob_chara_item_equip::set_step(float_t value) { + step = value; +} + +void rob_chara_item_equip::set_texture_pattern(texture_pattern_struct* tex_pat, size_t count) { + texture_pattern.clear(); + if (count && tex_pat) + for (size_t i = 0; i < count; i++) + texture_pattern.push_back(tex_pat[i]); +} + +void rob_chara_item_equip::skp_load(item_id id, skin_param_osage_root& skp_root, + std::vector& vec, skin_param_file_data* skp_file_data, bone_database* bone_data) { + if (id >= ITEM_BODY && id < ITEM_MAX) + item_equip_object[id].skp_load(skp_root, vec, skp_file_data, bone_data); +} + item_cos_texture_change_tex::item_cos_texture_change_tex() : org(), chg(), changed() { } @@ -13601,6 +12926,652 @@ rob_chara_item_cos_data::~rob_chara_item_cos_data() { } +bool rob_chara_item_cos_data::check_for_npr_flag() { + for (int32_t i = ITEM_SUB_ZUJO; i < ITEM_SUB_MAX; i++) { + const item_table_item* item = item_table_handler_array_get_item(chara_index, cos.arr[i]); + if (item && item->npr_flag) + return true; + } + return false; +} + +const item_cos_data* rob_chara_item_cos_data::get_cos() { + return &cos; +} + +object_info rob_chara_item_cos_data::get_head_object_replace(int32_t head_object_id) { + auto elem = head_replace.find(head_object_id); + if (elem != head_replace.end()) + return elem->second; + return {}; +} + +float_t rob_chara_item_cos_data::get_max_face_depth() { + float_t max_face_depth = 0.0f; + for (int32_t i = ITEM_SUB_ZUJO; i < ITEM_SUB_MAX; i++) { + const item_table_item* item = item_table_handler_array_get_item(chara_index, cos.arr[i]); + if (item && max_face_depth < item->face_depth) + max_face_depth = item->face_depth; + } + return max_face_depth; +} + +static int32_t item_cos_tex_chg_counter = 0; + +static texture_id item_cos_tex_chg_counter_get(texture_id id) { + if (id != texture_id(0x30, 0)) + return id; + + texture_id tex_id = texture_id(0x30, item_cos_tex_chg_counter); + if (item_cos_tex_chg_counter < 0xFFFFFFF) + item_cos_tex_chg_counter++; + else + item_cos_tex_chg_counter = 0; + return tex_id; +} + +static void sub_140526FD0(rob_chara_item_cos_data* item_cos_data, + int32_t item_no, const item_table_item* item) { + if (!(item->attr & 0x0C)) + return; + + if (!(item->attr & 0x08)) { + std::vector tex_chg_vec; + for (const item_table_item_data_tex& i : item->data.tex) { + item_cos_texture_change_tex chg_tex; + chg_tex.org = texture_storage_get_texture(i.org); + chg_tex.chg = texture_storage_get_texture(i.chg); + chg_tex.changed = false; + tex_chg_vec.push_back(chg_tex); + } + item_cos_data->texture_change.insert({ item_no, tex_chg_vec }); + return; + } + else if (item->data.col.size() <= 0) + return; + + std::vector chg_tex_ids; + if (item->attr & 0x04) + for (const item_table_item_data_tex& i : item->data.tex) + chg_tex_ids.push_back(i.chg); + else + for (const item_table_item_data_col& i : item->data.col) + chg_tex_ids.push_back(i.tex_id); + + std::vector tex_chg_vec; + size_t index = 0; + for (int32_t& i : chg_tex_ids) { + size_t j = &i - chg_tex_ids.data(); + texture* tex = texture_storage_get_texture(i); + if (!tex) { + index++; + continue; + } + + bool changed = false; + if (item->data.col[j].flag & 0x01) { + tex = texture_copy(item_cos_tex_chg_counter_get(texture_id(0x30, 0)), tex); + texture_apply_color_tone(tex, tex, &item->data.col[j].col_tone); + changed = true; + } + + item_cos_texture_change_tex chg_tex; + chg_tex.org = texture_storage_get_texture(item->data.col[j].tex_id); + chg_tex.chg = tex; + chg_tex.changed = changed; + tex_chg_vec.push_back(chg_tex); + } + item_cos_data->texture_change.insert({ item_no, tex_chg_vec }); +} + +static void sub_140527280(rob_chara_item_cos_data* item_cos_data, + int32_t item_no, const item_table_item* item) { + if (!(item->attr & 0x20) || !(item->attr & 0xC)) + return; + + for (const item_table_item_data_obj& i : item->data.obj) { + if (i.rpk == ITEM_NONE) + continue; + + auto elem = item_cos_data->item_change.find(i.rpk); + if (elem == item_cos_data->item_change.end()) { + std::vector vec; + vec.push_back(item_no); + item_cos_data->item_change.insert({ i.rpk, vec }); + } + else { + std::vector vec = elem->second; + vec.push_back(item_no); + item_cos_data->item_change.insert_or_assign(i.rpk, vec); + } + } +} + +static void sub_140522990(rob_chara_item_cos_data* item_cos_data) { + item_cos_data->texture_change_clear(); + item_cos_data->texture_pattern_clear(); + for (int32_t sub_id = ITEM_SUB_ZUJO; sub_id < ITEM_SUB_MAX; sub_id++) { + int32_t item_no = item_cos_data->cos.arr[sub_id]; + if (!item_no || sub_id == ITEM_SUB_KUTSU) + continue; + + const item_table_item* item = item_table_handler_array_get_item(item_cos_data->chara_index, item_no); + if (!item) + continue; + + sub_140526FD0(item_cos_data, item_no, item); + sub_140527280(item_cos_data, item_no, item); + } +} + +static void sub_14052B4C0(rob_chara_item_cos_data* item_cos_data, + rob_chara_item_equip* rob_itm_equip, item_id id, bool object) { + if (object) + rob_itm_equip->set_texture_pattern(0, 0); + else + rob_itm_equip->set_object_texture_pattern(id, 0, 0); +} + +static void sub_14052CCC0(rob_chara_item_cos_data* item_cos_data, rob_chara_item_equip* rob_itm_equip, + std::vector& item_nos, item_id id, bool a5) { + if (id < ITEM_BODY || id >= ITEM_MAX) + return; + + for (uint32_t& i : item_nos) { + auto elem = item_cos_data->texture_change.find(i); + if (elem == item_cos_data->texture_change.end()) + continue; + + for (item_cos_texture_change_tex& j : elem->second) + if (j.org && j.chg) + item_cos_data->texture_pattern[id].push_back({ j.org->id, j.chg->id }); + } + + if (a5) + rob_itm_equip->set_texture_pattern( + item_cos_data->texture_pattern[id].data(), item_cos_data->texture_pattern[id].size()); + else + rob_itm_equip->set_object_texture_pattern(id, + item_cos_data->texture_pattern[id].data(), item_cos_data->texture_pattern[id].size()); +} + +static void rob_chara_item_equip_load_body_parts_object_info(rob_chara_item_equip* rob_itm_equip, + item_id id, object_info obj_info, bone_database* bone_data, void* data, object_database* obj_db) { + if (id <= ITEM_BODY || id >= ITEM_MAX) + obj_info = {}; + rob_itm_equip->load_object_info(obj_info, id, false, bone_data, data, obj_db); +} + +static void sub_140522A30(rob_chara_item_cos_data* item_cos_data, rob_chara_item_equip* rob_itm_equip, + bone_database* bone_data, void* data, object_database* obj_db) { + const chara_init_data* chr_init_data = chara_init_data_get(item_cos_data->chara_index); + for (int32_t i = ITEM_ATAMA; i < ITEM_KAMI; i++) { + rob_itm_equip->load_outfit_object_info((item_id)i, + chr_init_data->field_7E4[i - 1], false, bone_data, data, obj_db); + auto elem = item_cos_data->item_change.find(i); + if (elem == item_cos_data->item_change.end()) + sub_14052B4C0(item_cos_data, rob_itm_equip, (item_id)i, false); + else + sub_14052CCC0(item_cos_data, rob_itm_equip, elem->second, (item_id)i, false); + } + + for (int32_t i = ITEM_KAMI; i < ITEM_MAX; i++) { + rob_chara_item_equip_load_body_parts_object_info(rob_itm_equip, + (item_id)i, {}, bone_data, data, obj_db); + sub_14052B4C0(item_cos_data, rob_itm_equip, (item_id)i, false); + } + sub_14052B4C0(item_cos_data, rob_itm_equip, ITEM_NONE, true); + + item_cos_data->field_F0.clear(); + item_cos_data->field_100.clear(); + item_cos_data->head_replace.clear(); +} + +static item_id sub_140525B90(rob_chara_item_cos_data* item_cos_data, item_sub_id sub_id) { + const item_id v3[] = { + ITEM_ZUJO, ITEM_KAMI, ITEM_NONE, ITEM_NONE, + ITEM_MEGANE, ITEM_NONE, ITEM_KUCHI, ITEM_NONE, + ITEM_KUBI, ITEM_NONE, ITEM_OUTER, ITEM_NONE, + ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_NONE, + ITEM_JOHA_USHIRO, ITEM_NONE, ITEM_NONE, ITEM_PANTS, + ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_NONE, + ITEM_NONE, + }; + + return v3[sub_id]; +} + +static item_id sub_140512EF0(rob_chara_item_equip* rob_itm_eq, item_id id) { + int32_t v2 = id; + if (id < ITEM_BODY || id >= ITEM_ITEM16) + return ITEM_NONE; + + for (rob_chara_item_equip_object* i = &rob_itm_eq->item_equip_object[id]; + i->obj_info.not_null(); i++) { + if (++v2 >= ITEM_ITEM16) + return ITEM_NONE; + } + return (item_id)v2; +} + +static void sub_140513B90(rob_chara_item_equip* rob_itm_equip, item_id id, object_info a3, object_info a4, + bone_database* bone_data, void* data, object_database* obj_db) { + if (id <= ITEM_BODY || id >= ITEM_MAX) { + a4 = {}; + a3 = {}; + } + rob_itm_equip->field_D0 = a4; + rob_itm_equip->field_D4 = id; + rob_itm_equip->load_object_info(a3, id, false, bone_data, data, obj_db); + if (id >= ITEM_BODY && id < ITEM_MAX) + rob_itm_equip->item_equip_object[id].can_disp = false; +} + +static void sub_14052C560(rob_chara_item_cos_data* item_sub_data, + rob_chara_item_equip* rob_itm_equip, int32_t item_no, const item_table_item* item, item_sub_id sub_id, + bone_database* bone_data, void* data, object_database* obj_db) { + for (const item_table_item_data_obj& i : item->data.obj) { + if (i.obj_info.is_null()) + break; + + if (item->type == 1) { + if (i.rpk != ITEM_NONE) { + rob_itm_equip->load_outfit_object_info(i.rpk, + i.obj_info, false, bone_data, data, obj_db); + if (item->attr & 0xC) + item_sub_data->set_texture_pattern(rob_itm_equip, item_no, i.rpk, false); + } + continue; + } + + else if (item->type) + continue; + + item_id id = sub_140525B90(item_sub_data, sub_id); + if (id != ITEM_NONE) { + rob_chara_item_equip_load_body_parts_object_info(rob_itm_equip, + id, i.obj_info, bone_data, data, obj_db); + item_sub_data->field_F0.insert({ i.obj_info, id }); + if (item->attr & 0xC) + item_sub_data->set_texture_pattern(rob_itm_equip, item_no, id, false); + } + else { + item_id id = sub_140512EF0(rob_itm_equip, ITEM_ITEM09); + if (id != ITEM_NONE && ((item->attr & 0x10) == 0 || &i - item->data.obj.data())) { + bool v8 = false; + if (item->attr & 0x10 && &i - item->data.obj.data() == 1) { + sub_140513B90(rob_itm_equip, id, item->data.obj[1].obj_info, + item->data.obj[0].obj_info, bone_data, data, obj_db); + v8 = true; + } + else + rob_chara_item_equip_load_body_parts_object_info(rob_itm_equip, + id, i.obj_info, bone_data, data, obj_db); + item_sub_data->field_F0.insert({ i.obj_info, id }); + if (item->attr & 0xC) + item_sub_data->set_texture_pattern(rob_itm_equip, item_no, id, v8); + } + } + } + + item_sub_data->head_replace.clear(); + if (!(item->attr & 0x800)) + return; + + const char* chara_name = chara_index_get_chara_name(item_sub_data->chara_index); + char buf[0x200]; + for (int32_t i = 0; i < 9; i++) { + sprintf_s(buf, sizeof(buf), "%sITM%03d_ATAM_HEAD_%02d_SP__DIVSKN", chara_name, item_no, i); + object_info obj_info = obj_db->get_object_info(buf); + if (obj_info.not_null()) + item_sub_data->head_replace.insert({ i, obj_info }); + } +} + +static void sub_140522C60(rob_chara_item_cos_data* item_cos_data, rob_chara_item_equip* rob_itm_equip, + bone_database* bone_data, void* data, object_database* obj_db) { + if (!item_table_handler_array_get_table(item_cos_data->chara_index)) + return; + + const item_sub_id dword_140A2BB70[] = { + ITEM_SUB_ZUJO, ITEM_SUB_KAMI, ITEM_SUB_HITAI, ITEM_SUB_ME, ITEM_SUB_MEGANE, + ITEM_SUB_MIMI, ITEM_SUB_KUCHI, ITEM_SUB_MAKI, ITEM_SUB_KUBI, ITEM_SUB_INNER, + ITEM_SUB_JOHA_MAE, ITEM_SUB_JOHA_USHIRO, ITEM_SUB_HADA, ITEM_SUB_U_UDE, ITEM_SUB_L_UDE, + ITEM_SUB_TE, ITEM_SUB_BELT, ITEM_SUB_ASI, ITEM_SUB_SUNE, ITEM_SUB_HEAD, ITEM_SUB_MAX, + }; + + const item_sub_id* v6 = dword_140A2BB70; + while (*v6 != ITEM_SUB_MAX) { + int32_t item_no = item_cos_data->cos.arr[*v6]; + if (item_no) { + const item_table_item* item = item_table_handler_array_get_item(item_cos_data->chara_index, item_no); + if (item && item->type != 3 && item->attr & 0x01) + sub_14052C560(item_cos_data, rob_itm_equip, item_no, item, *v6, bone_data, data, obj_db); + } + v6++; + } +} + +static void sub_14052C8C0(rob_chara_item_cos_data* item_cos_data, rob_chara_item_equip* rob_itm_equip, + int32_t item_no, const item_table_item* item, bone_database* bone_data, void* data, object_database* obj_db) { + for (const item_table_item_data_obj& i : item->data.obj) { + object_info v11 = i.obj_info; + item_id id = i.rpk; + if (item->type == 1) { + if (v11.is_null()) { + if (id == ITEM_NONE) + continue; + } + else if (id == ITEM_NONE) { + } + else if (i.rpk < 100 || i.rpk > 105) { + rob_itm_equip->load_outfit_object_info(id, + v11, false, bone_data, data, obj_db); + } + else { + item_id v13 = sub_140512EF0(rob_itm_equip, ITEM_KAMI); + if (v13 == ITEM_NONE) + continue; + + rob_chara_item_equip_load_body_parts_object_info(rob_itm_equip, + v13, i.obj_info, bone_data, data, obj_db); + item_cos_data->field_F0.insert({ i.obj_info, v13 }); + item_cos_data->field_100.insert({ id, v13 }); + id = v13; + } + } + else if (item->type == 0) { + id = sub_140512EF0(rob_itm_equip, ITEM_ITEM09); + if (id != ITEM_NONE) { + rob_chara_item_equip_load_body_parts_object_info(rob_itm_equip, + id, i.obj_info, bone_data, data, obj_db); + item_cos_data->field_F0.insert({ i.obj_info, id }); + continue; + } + } + else + continue; + + if (item->attr & 0xC) + item_cos_data->set_texture_pattern(rob_itm_equip, item_no, id, false); + } +} + +static void sub_140522D00(rob_chara_item_cos_data* item_cos_data, rob_chara_item_equip* rob_itm_equip, + bone_database* bone_data, void* data, object_database* obj_db) { + if (!item_table_handler_array_get_table(item_cos_data->chara_index)) + return; + + const item_sub_id dword_140A2BBC8[] = { + ITEM_SUB_KATA, ITEM_SUB_PANTS, ITEM_SUB_MAX, + }; + + const item_sub_id* v6 = dword_140A2BBC8; + while (*v6 != ITEM_SUB_MAX) { + int32_t item_no = item_cos_data->cos.arr[*v6]; + if (item_no) { + const item_table_item* item = item_table_handler_array_get_item(item_cos_data->chara_index, item_no); + if (item && item->type != 3 && item->attr & 0x01) + sub_14052C8C0(item_cos_data, rob_itm_equip, item_no, item, bone_data, data, obj_db); + } + v6++; + } +} + +static void sub_140525D90(rob_chara_item_cos_data* item_cos_data, rob_chara_item_equip* rob_itm_equip, + int32_t a3, bone_database* bone_data, void* data, object_database* obj_db) { + auto elem = item_cos_data->field_100.find(a3); + if (elem != item_cos_data->field_100.end()) + rob_chara_item_equip_load_body_parts_object_info(rob_itm_equip, + (item_id)elem->second, {}, bone_data, data, obj_db); +} + +static void sub_140522D90(rob_chara_item_cos_data* item_cos_data, rob_chara_item_equip* rob_itm_equip, + bone_database* bone_data, void* data, object_database* obj_db) { + if (!item_table_handler_array_get_table(item_cos_data->chara_index)) + return; + + const item_sub_id dword_140A2BBD8[] = { + ITEM_SUB_COSI, ITEM_SUB_OUTER, ITEM_SUB_MAX, + }; + + const item_sub_id* v6 = dword_140A2BBD8; + while (*v6 != ITEM_SUB_MAX) { + int32_t item_no = item_cos_data->cos.arr[*v6]; + if (!item_no) { + v6++; + continue; + } + + const item_table_item* item = item_table_handler_array_get_item(item_cos_data->chara_index, item_no); + if (!item || item->type != 1) + continue; + + for (const item_table_item_data_obj& i : item->data.obj) { + if (i.obj_info.is_null()) + continue; + + item_id id = sub_140525B90(item_cos_data, *v6); + rob_chara_item_equip_load_body_parts_object_info(rob_itm_equip, + id, i.obj_info, bone_data, data, obj_db); + item_cos_data->field_F0.insert({ i.obj_info, id }); + if (item->attr & 0xC) + item_cos_data->set_texture_pattern(rob_itm_equip, item_no, id, false); + + if (i.rpk == ITEM_NONE) + continue; + + rob_itm_equip->load_outfit_object_info(i.rpk, + {}, false, bone_data, data, obj_db); + switch (i.rpk) { + case ITEM_UDE_R: + sub_140525D90(item_cos_data, rob_itm_equip, 102, bone_data, data, obj_db); + break; + case ITEM_UDE_L: + sub_140525D90(item_cos_data, rob_itm_equip, 100, bone_data, data, obj_db); + break; + case ITEM_TE_R: + sub_140525D90(item_cos_data, rob_itm_equip, 103, bone_data, data, obj_db); + break; + case ITEM_TE_L: + sub_140525D90(item_cos_data, rob_itm_equip, 101, bone_data, data, obj_db); + break; + case ITEM_SUNE: + sub_140525D90(item_cos_data, rob_itm_equip, 104, bone_data, data, obj_db); + break; + case ITEM_ASI: + sub_140525D90(item_cos_data, rob_itm_equip, 105, bone_data, data, obj_db); + break; + } + } + v6++; + } +} + +static void sub_140522F90(rob_chara_item_cos_data* item_cos_data, rob_chara_item_equip* rob_itm_equip) { + if (!item_table_handler_array_get_table(item_cos_data->chara_index)) + return; + + int32_t item_no = item_cos_data->cos.arr[ITEM_SUB_KUTSU]; + if (!item_no) + return; + + const item_table_item* item = item_table_handler_array_get_item(item_cos_data->chara_index, item_no); + if (!item || item->data.col.size()) + return; + + vec3 texture_color_coefficients = item->data.col[0].col_tone.blend; + vec3 texture_color_offset = item->data.col[0].col_tone.offset; + vec3 texture_specular_coefficients = 1.0f; + vec3 texture_specular_offset = 0.0f; + if (item->data.col.size() > 1) { + texture_specular_coefficients = item->data.col[1].col_tone.blend; + texture_specular_offset = item->data.col[1].col_tone.offset; + } + + for (int32_t i = ITEM_ATAMA; i <= ITEM_ITEM16; i++) { + if (rob_itm_equip->get_object_info((item_id)i).is_null()) + continue; + + rob_chara_item_equip_object* itm_eq_obj = &rob_itm_equip->item_equip_object[i]; + itm_eq_obj->texture_data.field_0 = false; + itm_eq_obj->texture_data.texture_color_coefficients = texture_color_coefficients; + itm_eq_obj->texture_data.texture_color_offset = texture_color_offset; + itm_eq_obj->texture_data.texture_specular_coefficients = texture_specular_coefficients; + itm_eq_obj->texture_data.texture_specular_offset = texture_specular_offset; + } +} + +static void sub_140523230(rob_chara_item_cos_data* item_cos_data, + rob_chara_item_equip* rob_itm_equip, int32_t item_no) { + if (!item_no) + return; + + const item_table_item* item = item_table_handler_array_get_item(item_cos_data->chara_index, item_no); + if (!item || !((item->type + 1) & ~0x04)) + return; + + if (item->attr & 0x02) + for (const item_table_item_data_ofs& i : item->data.ofs) { + if (item_cos_data->cos.arr[i.sub_id] != i.no) + continue; + + for (const item_table_item_data_obj& j : item->data.obj) { + if (j.obj_info.is_null()) + break; + + auto elem = item_cos_data->field_F0.find(j.obj_info); + if (elem != item_cos_data->field_F0.end()) + rob_itm_equip->set_null_blocks_expression_data( + elem->second, i.position, i.rotation, i.scale); + } + break; + } + + for (const item_table_item_data_obj& i : item->data.obj) { + if (i.obj_info.is_null()) + break; + + auto elem = item_cos_data->field_F0.find(i.obj_info); + if (elem != item_cos_data->field_F0.end()) { + item_id id = elem->second; + sub_140514130(rob_itm_equip, id, !(item->attr & 0x80)); + sub_140514110(rob_itm_equip, id, !(item->attr & 0x100)); + sub_140513C60(rob_itm_equip, id, !(item->attr & 0x400)); + } + } +} + +static void sub_1405231D0(rob_chara_item_cos_data* item_cos_data, rob_chara_item_equip* rob_itm_equip) { + if (!item_table_handler_array_get_table(item_cos_data->chara_index)) + return; + + for (int32_t i = ITEM_SUB_ZUJO; i < ITEM_SUB_MAX; i++) + sub_140523230(item_cos_data, rob_itm_equip, item_cos_data->cos.arr[i]); +} + +static void sub_1405234E0(rob_chara_item_cos_data* item_cos_data) { + item_cos_data->item_change.clear(); + item_cos_data->field_F0.clear(); + item_cos_data->field_100.clear(); +} + +void rob_chara_item_cos_data::reload_items(int32_t chara_id, + bone_database* bone_data, void* data, object_database* obj_db) { + rob_chara_item_equip* rob_itm_equip = rob_chara_array_get_item_equip(chara_id); + sub_140522990(this); + sub_140522A30(this, rob_itm_equip, bone_data, data, obj_db); + sub_140522C60(this, rob_itm_equip, bone_data, data, obj_db); + sub_140522D00(this, rob_itm_equip, bone_data, data, obj_db); + sub_140522D90(this, rob_itm_equip, bone_data, data, obj_db); + sub_140522F90(this, rob_itm_equip); + sub_1405231D0(this, rob_itm_equip); + sub_1405234E0(this); +} + +void rob_chara_item_cos_data::set_chara_index(::chara_index chara_index) { + this->chara_index = chara_index; +} + +void rob_chara_item_cos_data::set_chara_index_item(::chara_index chara_index, int32_t item_no) { + set_chara_index(chara_index); + set_item(item_no); +} + +void rob_chara_item_cos_data::set_chara_index_item_nos(::chara_index chara_index, const int32_t* items) { + set_chara_index(chara_index); + set_item_nos(items); +} + +void rob_chara_item_cos_data::set_chara_index_item_zero(::chara_index chara_index, int32_t item_no) { + set_chara_index(chara_index); + set_item_zero(item_no); +} + +void rob_chara_item_cos_data::set_item(int32_t item_no) { + if (!item_no) + return; + + const item_table_item* item = item_table_handler_array_get_item(chara_index, item_no); + if (item) + cos.arr[item->sub_id] = item_no; +} + +void rob_chara_item_cos_data::set_item_array(rob_chara_pv_data_item* item) { + for (int32_t i = 0; i < 4; i++) + set_item(item->arr[i]); +} + +void rob_chara_item_cos_data::set_item_no(item_sub_id sub_id, int32_t item_no) { + cos.arr[sub_id] = item_no; +} + +void rob_chara_item_cos_data::set_item_nos(const int32_t* item_nos) { + for (int32_t i = ITEM_SUB_ZUJO; i < ITEM_SUB_MAX; i++) + set_item_no((item_sub_id)i, item_nos[i]); +} + +void rob_chara_item_cos_data::set_item_zero(int32_t item_no) { + const item_table_item* item = item_table_handler_array_get_item(chara_index, item_no); + if (item) + cos.arr[item->sub_id] = 0; +} + +void rob_chara_item_cos_data::set_texture_pattern(rob_chara_item_equip* rob_itm_equip, + uint32_t item_no, item_id id, bool tex_pat_for_all) { + if (id < ITEM_BODY || id >= ITEM_MAX) + return; + + auto elem = texture_change.find(item_no); + if (elem == texture_change.end()) + return; + + for (item_cos_texture_change_tex& i : elem->second) + if (i.org && i.chg) + texture_pattern[id].push_back({ i.org->id, i.chg->id }); + + if (tex_pat_for_all) + rob_itm_equip->set_texture_pattern(texture_pattern[id].data(), texture_pattern[id].size()); + else + rob_itm_equip->set_object_texture_pattern(id, + texture_pattern[id].data(), texture_pattern[id].size()); +} + +void rob_chara_item_cos_data::texture_change_clear() { + for (auto i : texture_change) + for (item_cos_texture_change_tex& j : i.second) + if (j.changed) { + texture_free(j.chg); + j.chg = 0; + } + texture_change.clear(); +} + +void rob_chara_item_cos_data::texture_pattern_clear() { + for (std::vector& i : texture_pattern) + i.clear(); +} + struc_523::struc_523() : field_0(), field_1(), field_4(), field_8(), field_C(), field_10(), field_1C(), field_20(), field_24(), field_28(), field_2C(), field_30(), field_34(), field_38(), field_3C(), field_40(), field_44(), field_48(), field_4C(), @@ -14077,8 +14048,8 @@ void rob_chara_motion::reset() { field_150.Reset(); hand_l.Reset(); hand_r.Reset(); - hand_l_object = object_info(); - hand_r_object = object_info(); + hand_l_object = {}; + hand_r_object = {}; field_3B0.Reset(); for (rob_chara_data_adjust& i : parts_adjust) @@ -14520,72 +14491,18 @@ rob_chara::~rob_chara() { item_equip = 0; } -inline void motion_storage_init() { - motion_storage_data = {}; -} - -inline bool motion_storage_check_mot_file_not_ready(uint32_t set_id) { - auto elem = motion_storage_data.find(set_id); - if (elem != motion_storage_data.end()) - return elem->second.CheckNotReady(); - return false; -} - -inline const mot_data* motion_storage_get_mot_data(uint32_t motion_id, motion_database* mot_db) { - uint32_t set_id = -1; - size_t motion_index = -1; - for (motion_set_info& i : mot_db->motion_set) { - for (motion_info& j : i.motion) - if (j.id == motion_id) { - set_id = i.id; - motion_index = &j - i.motion.data(); - - break; - } - - if (set_id != -1) - break; - } - - if (set_id == -1) - return 0; - - auto elem = motion_storage_data.find(set_id); - if (elem != motion_storage_data.end()) - return &elem->second.mot_set->mot_data[motion_index]; - return 0; -} - -inline float_t motion_storage_get_mot_data_frame_count(uint32_t motion_id, motion_database* mot_db) { - const mot_data* mot = motion_storage_get_mot_data(motion_id, mot_db); - if (mot) - return mot->frame_count; - return 0.0f; -} - -inline const mot_set* motion_storage_get_motion_set(uint32_t set_id) { - auto elem = motion_storage_data.find(set_id); - if (elem != motion_storage_data.end()) - return elem->second.mot_set; - return 0; -} - -inline void motion_storage_free() { - motion_storage_data.clear(); -} - -inline void mothead_storage_init() { +void mothead_storage_init() { mothead_storage_data = {}; } -inline bool mothead_storage_check_mhd_file_not_ready(uint32_t set_id) { +bool mothead_storage_check_mhd_file_not_ready(uint32_t set_id) { auto elem = mothead_storage_data.find(set_id); if (elem != mothead_storage_data.end()) return elem->second.CheckNotReady(); return false; } -inline const mothead_mot* mothead_storage_get_mot_by_motion_id(uint32_t motion_id, motion_database* mot_db) { +const mothead_mot* mothead_storage_get_mot_by_motion_id(uint32_t motion_id, motion_database* mot_db) { uint32_t set_id = mot_db->get_motion_set_id_by_motion_id(motion_id); if (set_id == -1) return &mothead_mot_null; @@ -14604,17 +14521,10 @@ inline const mothead_mot* mothead_storage_get_mot_by_motion_id(uint32_t motion_i return mot; } -inline void mothead_storage_free() { +void mothead_storage_free() { mothead_storage_data.clear(); } -bool skin_param_manager_array_check_task_ready() { - bool ret = false; - for (int32_t i = 0; i < ROB_CHARA_COUNT; i++) - ret |= app::TaskWork::CheckTaskReady(&skin_param_manager[i]); - return ret; -} - int32_t expression_id_to_mottbl_index(int32_t expression_id) { static const int32_t expression_id_to_mottbl_index_table[] = { 11, 15, 57, 19, 23, 25, 29, 33, @@ -14691,82 +14601,6 @@ int32_t mouth_anim_id_to_mottbl_index(int32_t mouth_anim_id) { return 131; } -MotFile::MotFile() : mot_set_info(), mot_set(), load_count() { - FreeData(); -} - -MotFile:: ~MotFile() { - FreeData(); -} - -bool MotFile::CheckNotReady() { - return file_handler.check_not_ready(); -} - -void MotFile::FreeData() { - mot_set_info = 0; - alloc_handler.reset(); - mot_set = 0; - file_handler.reset(); - load_count = 0; -} - -void MotFile::LoadFile(std::string* mdata_dir, uint32_t set) { - if (load_count > 0) { - load_count++; - return; - } - - data_struct* aft_data = &data_list[DATA_AFT]; - - std::string rom_dir = "rom/"; - std::string rob_dir = "rob/"; - std::string farc_file = "mot_" + mot_set_info->name + ".farc"; - std::string mot_file = "mot_" + mot_set_info->name + ".bin"; - if (mdata_dir && mdata_dir->size()) - if (aft_data->check_file_exists(mdata_dir->c_str(), farc_file.c_str())) { - rom_dir = *mdata_dir; - rob_dir.clear(); - } - - std::string dir = rom_dir + rob_dir; - if (aft_data->check_file_exists(dir.c_str(), farc_file.c_str())) { - if (file_handler.read_file(aft_data, dir.c_str(), farc_file.c_str(), mot_file.c_str(), false)) - file_handler.set_callback_data(0, (PFNFILEHANDLERCALLBACK*)ParseFileParent, this); - } - else { - if (file_handler.read_file(aft_data, rom_dir.c_str(), mot_file.c_str())) - file_handler.set_callback_data(0, (PFNFILEHANDLERCALLBACK*)ParseFileParent, this); - } - load_count = 1; -} - -void MotFile::ParseFile(const void* data, size_t size) { - prj::shared_ptr& alloc = alloc_handler; - alloc = prj::shared_ptr(new alloc_data); - - ::mot_set* set = alloc->allocate<::mot_set>(); - mot_set = set; - set->unpack_file(alloc, data, size, false); -} - -bool MotFile::Unload() { - if (--load_count < 0) { - load_count = 0; - return true; - } - else if (load_count <= 0) { - FreeData(); - return true; - } - else - return false; -} - -void MotFile::ParseFileParent(MotFile* mot, const void* file_data, size_t size) { - mot->ParseFile(file_data, size); -} - MhdFile::MhdFile() : data(), set(), load_count() { FreeData(); } @@ -14941,6 +14775,13 @@ OpdMaker::Data::~Data() { Reset(); } +bool OpdMaker::Data::CheckOpdiFilesNotReady() { + for (auto i : opdi_files) + if (i.second->check_not_ready()) + return true; + return false; +} + const void* OpdMaker::Data::GetOpdiFileData(object_info obj_info, int32_t motion_id) { auto elem = opdi_files.find({ obj_info, motion_id }); if (elem != opdi_files.end()) @@ -15939,21 +15780,6 @@ static void sub_140479AA0(OpdMaker::Data* a1, rob_chara* rob_chr, std::vectorempty = false; } -static bool sub_14047A370(OpdMaker::Data* a1) { - for (auto i : a1->opdi_files) - if (i.second->check_not_ready()) - return true; - return false; -} - -static bool sub_140610780(int32_t chara_id, std::vector& vec) { - return skin_param_manager[chara_id].sub_140610480(vec); -} - -static bool sub_140611690(int32_t chara_id) { - return app::TaskWork::CheckTaskReady(&skin_param_manager[chara_id]); -} - static bool sub_140479090(struc_655* a1, chara_index chara_index) { std::vector* v4 = a1->field_10[chara_index].field_0; for (int32_t i = 0; i < ITEM_SUB_MAX; i++, v4++) @@ -15977,19 +15803,19 @@ bool OpdMakeWorker::Ctrl() { int32_t* v7 = field_70; for (int32_t i = 0; i < ITEM_SUB_MAX; i++, v7++) { - rob_chara_item_cos_data_set_chara_index(&rob_chr->item_cos_data, chara_index); - rob_chara_item_cos_data_set_item(&rob_chr->item_cos_data, *v7); + rob_chr->item_cos_data.set_chara_index(chara_index); + rob_chr->item_cos_data.set_item(*v7); } - rob_chara_item_equip_reset_init_data(rob_chr->item_equip, - rob_chara_bone_data_get_node(rob_chara_array[chara_id].bone_data, 0)); - rob_chara_item_equip_set_shadow_type(rob_chr->item_equip, chara_id); + rob_chr->item_equip->reset_init_data(rob_chara_bone_data_get_node( + rob_chara_array[chara_id].bone_data, 0)); + rob_chr->item_equip->set_shadow_type(chara_id); rob_chr->item_equip->field_A0 = 5; state = 2; } case 2: { if (!task_rob_load_check_load_req_data()) { - task_rob_load->AppendLoadReqDataObj(chara_index, &rob_chr->item_cos_data.cos); + task_rob_load_append_load_req_data_obj(chara_index, rob_chr->item_cos_data.get_cos()); state = 3; } } return false; @@ -15997,7 +15823,7 @@ bool OpdMakeWorker::Ctrl() { if (task_rob_load_check_load_req_data()) return false; - rob_chara_item_cos_data_reload_items(&rob_chr->item_cos_data, chara_id, aft_bone_data, aft_data, aft_obj_db); + rob_chr->item_cos_data.reload_items(chara_id, aft_bone_data, aft_data, aft_obj_db); for (int32_t i = ITEM_KAMI; i < ITEM_MAX; i++) { rob_chara_item_equip_object* itm_eq_obj = rob_chr->item_equip->get_item_equip_object((item_id)i); @@ -16024,24 +15850,20 @@ bool OpdMakeWorker::Ctrl() { state = 5; } break; case 5: { - if (sub_14047A370(&data)) + if (data.CheckOpdiFilesNotReady()) return false; state = 6; } case 6: { - std::vector v56; - for (int32_t& i : opd_make_manager->motion_ids) { - struc_462 v60; - v60.rob_chr = rob_chr; - v60.motion_id = i; - v56.push_back(v60); - } - sub_140610780(chara_id, v56); + std::vector osage_init; + for (int32_t& i : opd_make_manager->motion_ids) + osage_init.push_back(osage_init_data(rob_chr, i)); + skin_param_manager_add_task(chara_id, osage_init); state = 7; } break; case 7: { - if (sub_140611690(chara_id)) + if (skin_param_manager_check_task_ready(chara_id)) return false; state = 8; } @@ -16095,15 +15917,15 @@ bool OpdMakeWorker::Ctrl() { state = 10; } case 10: { - task_rob_load->AppendFreeReqDataObj(chara_index, &rob_chr->item_cos_data.cos); + task_rob_load_append_free_req_data_obj(chara_index, rob_chr->item_cos_data.get_cos()); int32_t* v45 = field_70; for (int32_t i = 0; i < ITEM_SUB_MAX; i++, v45++) { - rob_chara_item_cos_data_set_chara_index(&rob_chr->item_cos_data, chara_index); - rob_chara_item_cos_data_set_item_zero(&rob_chr->item_cos_data, *v45); + rob_chr->item_cos_data.set_chara_index(chara_index); + rob_chr->item_cos_data.set_item_zero(*v45); } - rob_chara_item_cos_data_reload_items(&rob_chr->item_cos_data, chara_id, aft_bone_data, aft_data, aft_obj_db); + rob_chr->item_cos_data.reload_items(chara_id, aft_bone_data, aft_data, aft_obj_db); skin_param_manager_reset(chara_id); if (sub_140479090(&opd_make_manager->field_88, chara_index)) return true; @@ -16128,6 +15950,14 @@ void OpdMakeWorker::Disp() { } +bool OpdMakeWorker::AddTask(bool a2) { + if (!task_rob_manager_check_chara_loaded(chara_id)) + return false; + + field_D4 = a2; + return app::TaskWork::AddTask(this, "OPD_MAKE_WORKER"); +} + struc_527::struc_527(rob_chara* rob_chr, int32_t stage_index, uint32_t motion_id, float_t frame, bone_database* bone_data, motion_database* mot_db) : rob_chr(), motion_id(), frame(), last_frame(), type_62(), type_62_data(), type_75(), type_75_data(), @@ -16202,7 +16032,7 @@ OpdMakeManager::~OpdMakeManager() { bool OpdMakeManager::Init() { mode = 1; - rctx_ptr->draw_pass.set_enable(DRAW_PASS_3D, false); + rctx_ptr->render_manager.set_pass_sw(rndr::RND_PASSID_ALL_3D, false); if (path_check_directory_exists("ram/osage_play_data")) RemoveDirectoryA("ram/osage_play_data"); @@ -16210,23 +16040,15 @@ bool OpdMakeManager::Init() { CreateDirectoryA("ram/osage_play_data", 0); if (field_1888 || !app::TaskWork::CheckTaskReady(task_rob_manager)) { - task_rob_manager_append_task(); + task_rob_manager_add_task(); return true; } else { - task_rob_manager_free_task(); + task_rob_manager_del_task(); return false; } } -bool sub_140479880(OpdMakeWorker* a1, bool a2) { - if (!task_rob_manager_check_chara_loaded(a1->chara_id)) - return false; - - a1->field_D4 = a2; - return app::TaskWork::AppendTask(a1, "OPD_MAKE_WORKER"); -} - bool OpdMakeManager::Ctrl() { data_struct* aft_data = &data_list[DATA_AFT]; motion_database* aft_mot_db = &aft_data->data_ft.mot_db; @@ -16240,19 +16062,13 @@ bool OpdMakeManager::Ctrl() { motion_set_ids.clear(); for (int32_t& i : motion_ids) { uint32_t set_id = aft_mot_db->get_motion_set_id_by_motion_id(i); - if (set_id == -1) - continue; - bool found = false; - for (uint32_t& j : motion_set_ids) - if (j == set_id) { - found = false; - break; - } - - if (!found) + if (set_id != -1) motion_set_ids.push_back(set_id); } + prj::sort(motion_set_ids); + prj::unique(motion_set_ids); + for (uint32_t& i : motion_set_ids) { motion_set_load_motion(i, 0, aft_mot_db); motion_set_load_mothead(i, 0, aft_mot_db); @@ -16299,7 +16115,7 @@ bool OpdMakeManager::Ctrl() { v11 = true; if (!v11) { - task_rob_manager->sub_14019C540(); + task_rob_manager->HideTask(); mode = 7; } } break; @@ -16307,7 +16123,7 @@ bool OpdMakeManager::Ctrl() { int32_t j = 0; for (OpdMakeWorker*& i : workers) if (rob_chara_array_get(j++)) - sub_140479880(i, field_1888); + i->AddTask(field_1888); mode = 8; } break; case 8: { @@ -16322,7 +16138,7 @@ bool OpdMakeManager::Ctrl() { case 9: for (int32_t i = 0; i < 4; i++) rob_chara_array_free_chara_id(i); - task_rob_manager->sub_14019C3B0(); + task_rob_manager->RunTask(); mode = 10; break; case 10: @@ -16358,17 +16174,17 @@ bool OpdMakeManager::Dest() { rob_chara_array_free_chara_id(i); for (int32_t i = 0; i < ROB_CHARA_COUNT; i++) - skin_param_manager[i].Reset(); + skin_param_manager_reset(i); for (uint32_t& i : motion_set_ids) { motion_set_unload_motion(i); motion_set_unload_mothead(i); } - task_rob_manager_free_task(); + task_rob_manager_del_task(); } - rctx_ptr->draw_pass.set_enable(DRAW_PASS_3D, true); + rctx_ptr->render_manager.set_pass_sw(rndr::RND_PASSID_ALL_3D, true); if (path_check_directory_exists("ram/osage_play_data")) RemoveDirectoryA("ram/osage_play_data"); @@ -16380,10 +16196,10 @@ void OpdMakeManager::Disp() { } -bool OpdMakeManager::TaskFree() { +bool OpdMakeManager::DelTask() { for (OpdMakeWorker*& i : workers) - i->SetDest(); - return SetDest(); + i->DelTask(); + return app::Task::DelTask(); } ReqData::ReqData() : chara_index(), count() { @@ -17084,238 +16900,6 @@ void RobImplTask::FreeInitCharaList(int8_t* chara_id) { FreeList(chara_id, &init_chara); } -struc_462::struc_462() : rob_chr() { - pv_id = -1; - motion_id = -1; - frame = -1; -} - -struc_462::~struc_462() { - -} - -static uint8_t skin_param_manager_index = 0; - -SkinParamManager::SkinParamManager() { - index = skin_param_manager_index++; - state = 1; -} - -SkinParamManager::~SkinParamManager() { - state = 1; - Reset(); -} - -bool SkinParamManager::Init() { - state = 0; - sub_14060FBD0(); - return true; -} - -bool SkinParamManager::Ctrl() { - if (state == 0) { - if (sub_1406112F0()) - return false; - state = 1; - } - else if (state != 1) - return false; - return true; -} - -bool SkinParamManager::Dest() { - return true; -} - -void SkinParamManager::Disp() { - -} - -void SkinParamManager::Reset() { - field_70.clear(); - - for (auto i : field_88) { - if (!i.second) - continue; - - i.second->clear(); - i.second->shrink_to_fit(); - delete i.second; - i.second = 0; - } - field_88.clear(); - - for (auto i : field_98) { - if (!i.second) - continue; - - i.second->clear(); - i.second->shrink_to_fit(); - delete i.second; - i.second = 0; - } - field_98.clear(); - - for (auto i : field_A8) { - if (!i.second) - continue; - - i.second->clear(); - i.second->shrink_to_fit(); - delete i.second; - i.second = 0; - } - field_A8.clear(); - - for (struc_461*& i : field_B8) { - if (i->file_handler) { - delete i->file_handler; - i->file_handler = 0; - } - delete i; - } - field_B8.clear(); - - Task::SetDest(); -} - -void SkinParamManager::sub_14060FBD0() { - if (!field_70.size()) - return; - - data_struct* aft_data = &data_list[DATA_AFT]; - object_database* aft_obj_db = &aft_data->data_ft.obj_db; - - field_B8.clear(); - for (struc_462& i : field_70) { - rob_chara_item_equip* rob_itm_equip = i.rob_chr->item_equip; - for (int32_t j = ITEM_KAMI; j < ITEM_MAX; j++) { - object_info v9 = rob_chara_item_equip_get_object_info(rob_itm_equip, (item_id)j); - if (!v9.is_null()) - continue; - - const char* v11 = aft_obj_db->get_object_name(v9); - if (!v11) - continue; - - obj_skin* skin = object_storage_get_obj_skin(v9); - if (!skin || !skin->ex_data) - return; - - obj_skin_ex_data* ex_data = skin->ex_data; - int32_t osage_count = 0; - int32_t cloth_count = 0; - - obj_skin_block* b = ex_data->block_array; - for (int32_t k = ex_data->num_block; k; k--, b++) - if (b->type == OBJ_SKIN_BLOCK_OSAGE) - osage_count++; - else if (b->type == OBJ_SKIN_BLOCK_CLOTH) - cloth_count++; - - if (!ex_data || !cloth_count + osage_count) - continue; - - std::string v64 = "ext_skp_" + std::string(v11) + ".txt"; - struc_461* v19 = new struc_461; - if (!v19) - continue; - - v19->id = (item_id)j; - v19->rob_chr = i.rob_chr; - v19->field_C = -1; - - bool v17 = false; - std::string v61; - std::string v62; - if (i.name.size()) - //v19->field_C = sub_140610A90(this, &i, &v64, &i.name, &v61); - - if (v19->field_C == -1) { - std::string v63; - //v19->field_C = sub_14060B720(sp_skp_db_data_get(), &i, &v9, &v62, &v63); - if (v19->field_C == -1) { - delete v19; - continue; - } - - if (v19->field_C == 0 || v19->field_C == 1) { - v17 = true; - v61 = "rom/skin_param/" + v63; - } - else if (v19->field_C == 2) { - v61 = "rom/skin_param/" + v62; - } - } - - std::vector* v31 = 0; - if (v19->field_C == 0) { - std::pair> v49 = { v9, { i.motion_id, i.frame } }; - auto elem = field_88.find(v49); - if (elem != field_88.end()) { - delete v19; - continue; - } - - v31 = new std::vector; - field_88.insert({ v49, v31 }); - } - else if (v19->field_C == 1) { - std::pair> v49 = { v9, { i.motion_id, -1 } }; - auto elem = field_98.find(v49); - if (elem != field_98.end()) { - delete v19; - continue; - } - - v31 = new std::vector; - field_98.insert({ v49, v31 }); - } - else if (v19->field_C == 2) { - std::pair> v49 = { v9, { -1, -1 } }; - auto elem = field_A8.find(v49); - if (elem != field_A8.end()) { - delete v19; - continue; - } - - v31 = new std::vector; - field_A8.insert({ v49, v31 }); - } - - if (!v31) { - delete v19; - continue; - } - - v31->resize((size_t)cloth_count + osage_count); - v19->field_10 = v31; - - data_struct* aft_data = &data_list[DATA_AFT]; - - p_file_handler* pfhndl = new p_file_handler; - if (v17) - pfhndl->read_file(aft_data, 0, v61.c_str(), v62.c_str(), false); - else - pfhndl->read_file(aft_data, 0, v61.c_str()); - v19->file_handler = pfhndl; - field_B8.push_back(v19); - } - } -} - -bool SkinParamManager::sub_140610480(std::vector& vec) { - if (app::TaskWork::CheckTaskReady(this) || !app::TaskWork::AppendTask(this, "SKIN_PARAM_MANAGER")) - return false; - - field_70 = vec; - return true; -} - -bool SkinParamManager::sub_1406112F0() { - return false; -} - TaskRobBase::TaskRobBase() { } @@ -17629,7 +17213,7 @@ bool TaskRobLoad::AppendFreeReqData(chara_index chara_index) { return true; } -bool TaskRobLoad::AppendFreeReqDataObj(chara_index chara_index, item_cos_data* cos) { +bool TaskRobLoad::AppendFreeReqDataObj(chara_index chara_index, const item_cos_data* cos) { if (chara_index < CHARA_MIKU || chara_index > CHARA_TETO) return false; @@ -17668,7 +17252,7 @@ bool TaskRobLoad::AppendLoadReqData(chara_index chara_index) { return true; } -bool TaskRobLoad::AppendLoadReqDataObj(chara_index chara_index, item_cos_data* cos) { +bool TaskRobLoad::AppendLoadReqDataObj(chara_index chara_index, const item_cos_data* cos) { if (chara_index < CHARA_MIKU || chara_index > CHARA_TETO) return false; @@ -17817,12 +17401,12 @@ void TaskRobLoad::LoadCharaItem(chara_index chara_index, if (!item_no) return; - std::vector* item_objset = 0; - item_table_array_get_item_objset(chara_index, item_no, &item_objset); + const std::vector* item_objset + = item_table_handler_array_get_item_objset(chara_index, item_no); if (!item_objset) return; - for (uint32_t& i : *item_objset) + for (uint32_t i : *item_objset) if (i != (uint32_t)-1) object_storage_load_set(data, obj_db, i); } @@ -17831,12 +17415,12 @@ bool TaskRobLoad::LoadCharaItemCheckNotRead(chara_index chara_index, int32_t ite if (!item_no) return false; - std::vector* item_objset = 0; - item_table_array_get_item_objset(chara_index, item_no, &item_objset); + const std::vector* item_objset + = item_table_handler_array_get_item_objset(chara_index, item_no); if (!item_objset) return true; - for (uint32_t& i : *item_objset) + for (uint32_t i : *item_objset) if (i != (uint32_t)-1 && object_storage_load_obj_set_check_not_read(i)) return true; return false; @@ -17903,12 +17487,12 @@ void TaskRobLoad::UnloadCharaItem(chara_index chara_index, int32_t item_no) { if (!item_no) return; - std::vector* item_objset = 0; - item_table_array_get_item_objset(chara_index, item_no, &item_objset); + const std::vector* item_objset + = item_table_handler_array_get_item_objset(chara_index, item_no); if (!item_objset) return; - for (uint32_t& i : *item_objset) + for (uint32_t i : *item_objset) if (i != (uint32_t)-1) object_storage_unload_set(i); } @@ -17955,11 +17539,11 @@ TaskRobManager::~TaskRobManager() { } bool TaskRobManager::Init() { - app::TaskWork::AppendTask(task_rob_load, 0, "ROB_LOAD", 0); + task_rob_load_add_task(); rob_manager_rob_impl* rob_impls1 = rob_manager_rob_impls1_get(this); for (; rob_impls1->task; rob_impls1++) { RobImplTask* task = rob_impls1->task; - app::TaskWork::AppendTask(task, rob_impls1->name); + app::TaskWork::AddTask(task, rob_impls1->name); task->is_frame_dependent = task->IsFrameDependent(); task->FreeCharaLists(); } @@ -17967,7 +17551,7 @@ bool TaskRobManager::Init() { rob_manager_rob_impl* rob_impls2 = rob_manager_rob_impls2_get(this); for (; rob_impls2->task; rob_impls2++) { RobImplTask* task = rob_impls2->task; - app::TaskWork::AppendTask(task, rob_impls2->name); + app::TaskWork::AddTask(task, rob_impls2->name); task->is_frame_dependent = task->IsFrameDependent(); task->FreeCharaLists(); } @@ -18000,9 +17584,8 @@ bool TaskRobManager::Ctrl() { if (free_chara.size()) { for (rob_chara*& i : free_chara) { - chara_index chara_index = i->chara_index; - task_rob_load->AppendFreeReqData(chara_index); - task_rob_load->AppendFreeReqDataObj(chara_index, &i->item_cos_data.cos); + task_rob_load_append_free_req_data(i->chara_index); + task_rob_load_append_free_req_data_obj(i->chara_index, i->item_cos_data.get_cos()); FreeLoadedCharaList(&i->chara_id); } @@ -18035,9 +17618,8 @@ bool TaskRobManager::Ctrl() { continue; load_chara.push_back(i); - chara_index chara_index = i->chara_index; - task_rob_load->AppendLoadReqData(chara_index); - task_rob_load->AppendLoadReqDataObj(chara_index, &i->item_cos_data.cos); + task_rob_load_append_load_req_data(i->chara_index); + task_rob_load_append_load_req_data_obj(i->chara_index, i->item_cos_data.get_cos()); } init_chara.clear(); ctrl_state = 0; @@ -18049,11 +17631,11 @@ bool TaskRobManager::Dest() { if (dest_state == 0) { rob_manager_rob_impl* rob_impls1 = rob_manager_rob_impls1_get(this); for (; rob_impls1->task; rob_impls1++) - rob_impls1->task->SetDest(); + rob_impls1->task->DelTask(); rob_manager_rob_impl* rob_impls2 = rob_manager_rob_impls2_get(this); for (; rob_impls2->task; rob_impls2++) - rob_impls2->task->SetDest(); + rob_impls2->task->DelTask(); init_chara.clear(); load_chara.clear(); @@ -18064,7 +17646,7 @@ bool TaskRobManager::Dest() { else if (dest_state != 1) return false; - task_rob_load->SetDest(); + task_rob_load_del_task(); dest_state = 2; return true; } diff --git a/src/CRE/rob/rob.hpp b/src/CRE/rob/rob.hpp index f92179f5..eb7c17ac 100644 --- a/src/CRE/rob/rob.hpp +++ b/src/CRE/rob/rob.hpp @@ -1150,6 +1150,7 @@ struct motion_blend_mot { MotionBlendType get_type(); void reset(); + void set_step(float_t step); }; struct rob_chara_bone_data_ik_scale { @@ -1418,10 +1419,10 @@ public: virtual void Init() = 0; virtual void Field_10() = 0; - virtual void Field_18(int32_t stage, bool a3) = 0; + virtual void Field_18(int32_t stage, bool disable_external_force) = 0; virtual void Field_20() = 0; virtual void SetOsagePlayData() = 0; - virtual void Disp(mat4* mat, render_context* rctx) = 0; + virtual void Disp(const mat4* mat, render_context* rctx) = 0; virtual void Reset(); virtual void Field_40() = 0; virtual void Field_48() = 0; @@ -1441,10 +1442,10 @@ public: virtual void Init() override; virtual void Field_10(); - virtual void Field_18(int32_t stage, bool a3) override; + virtual void Field_18(int32_t stage, bool disable_external_force) override; virtual void Field_20() override; virtual void SetOsagePlayData() override; - virtual void Disp(mat4* mat, render_context* rctx) override; + virtual void Disp(const mat4* mat, render_context* rctx) override; virtual void Field_40() override; virtual void Field_48() override; virtual void Field_50() override; @@ -1503,6 +1504,8 @@ struct RobOsageNodeResetData { RobOsageNodeResetData(); }; +struct skin_param_osage_root; + struct RobOsageNodeData { float_t force; std::vector boc; @@ -1513,6 +1516,8 @@ struct RobOsageNodeData { ~RobOsageNodeData(); void Reset(); + void SetForce(skin_param_osage_root& skp_root, + skin_param_osage_node* skp_osg_node, size_t index); }; struct opd_vec3_data { @@ -1643,6 +1648,8 @@ struct skin_param { skin_param(); ~skin_param(); + void set_skin_param_osage_root(skin_param_osage_root& skp_root); + inline void reset() { coli.clear(); friction = 1.0f; @@ -1675,9 +1682,6 @@ struct OsageCollision { Work(); ~Work(); - int32_t osage_capsule_cls(vec3& p0, vec3& p1, const float_t& cls_r); - int32_t osage_cls(vec3& p, const float_t& cls_r); - static void update_cls_work(OsageCollision::Work* cls, SkinParam::CollisionParam* cls_param, mat4* tranform); static void update_cls_work(OsageCollision::Work* cls, @@ -1701,7 +1705,9 @@ struct OsageCollision { const vec3& p0, const vec3& p1, const OsageCollision::Work* cls, const float_t r); static int32_t cls_plane_oidashi(vec3& vec, const vec3& p, const vec3& p1, const vec3& p2, const float_t r); static void get_nearest_line2point(vec3& nearest, const vec3& p0, const vec3& p1, const vec3& q); + static int32_t osage_capsule_cls(const OsageCollision::Work* cls, vec3& p0, vec3& p1, const float_t& cls_r); static int32_t osage_capsule_cls(vec3& p0, vec3& p1, const float_t& cls_r, const OsageCollision::Work* cls); + static int32_t osage_cls(const OsageCollision::Work* cls, vec3& p, const float_t& cls_r); static int32_t osage_cls(vec3& p, const float_t& cls_r, const OsageCollision::Work* cls, float_t* fric = 0); static int32_t osage_cls_work_list(vec3& p, const float_t& cls_r, const OsageCollision& coli, float_t* fric = 0); }; @@ -1713,7 +1719,7 @@ struct osage_ring_data { float_t ring_rectangle_height; float_t ring_height; float_t ring_out_height; - bool field_18; + bool init; OsageCollision coli; std::vector skp_root_coli; @@ -1721,7 +1727,7 @@ struct osage_ring_data { ~osage_ring_data(); }; -struct struc_571; +struct skin_param_file_data; struct CLOTHNode { uint32_t flags; @@ -1837,7 +1843,7 @@ struct RobCloth : public CLOTH { void AddMotionResetData(int32_t motion_id, float_t frame); void ColiSet(mat4* mats); - void Disp(mat4* mat, render_context* rctx); + void Disp(const mat4* mat, render_context* rctx); void InitData(size_t root_count, size_t nodes_count, obj_skin_block_cloth_root* root, obj_skin_block_cloth_node* nodes, mat4* mats, int32_t a7, rob_chara_item_equip_object* itm_eq_obj, bone_database* bone_data); @@ -1870,10 +1876,10 @@ public: virtual void Init() override; virtual void Field_10(); - virtual void Field_18(int32_t stage, bool a3) override; + virtual void Field_18(int32_t stage, bool disable_external_force) override; virtual void Field_20() override; virtual void SetOsagePlayData() override; - virtual void Disp(mat4* mat, render_context* rctx) override; + virtual void Disp(const mat4* mat, render_context* rctx) override; virtual void Reset() override; virtual void Field_40() override; virtual void Field_48() override; @@ -1886,17 +1892,17 @@ public: void SetMotionResetData(int32_t motion_id, float_t frame); float_t* SetOsagePlayDataInit(float_t* opdi_data); void SetOsageReset(); - void SetSkinParam(struc_571* skp); + void SetSkinParam(skin_param_file_data* skp); void SetSkinParamOsageRoot(skin_param_osage_root* skp_root); }; -struct struc_571 { +struct skin_param_file_data { skin_param skin_param; std::vector nodes_data; bool field_88; - struc_571(); - ~struc_571(); + skin_param_file_data(); + ~skin_param_file_data(); }; struct osage_setting_osg_cat { @@ -1979,10 +1985,10 @@ public: virtual void Init() override; virtual void Field_10(); - virtual void Field_18(int32_t stage, bool a3) override; + virtual void Field_18(int32_t stage, bool disable_external_force) override; virtual void Field_20() override; virtual void SetOsagePlayData() override; - virtual void Disp(mat4* mat, render_context* rctx) override; + virtual void Disp(const mat4* mat, render_context* rctx) override; virtual void Reset() override; virtual void Field_40() override; virtual void Field_48() override; @@ -1997,7 +2003,7 @@ public: void SetMotionResetData(int32_t motion_id, float_t frame); float_t* SetOsagePlayDataInit(float_t* opdi_data); void SetOsageReset(); - void SetSkinParam(struc_571* skp); + void SetSkinParam(skin_param_file_data* skp); void SetWindDirection(); void sub_1405F3E10(obj_skin_block_osage* osg_data, obj_skin_osage_node* osg_nodes, std::vector>* a4, @@ -2017,10 +2023,10 @@ public: virtual void Init() override; virtual void Field_10() override; - virtual void Field_18(int32_t stage, bool a3) override; + virtual void Field_18(int32_t stage, bool disable_external_force) override; virtual void Field_20() override; virtual void SetOsagePlayData() override; - virtual void Disp(mat4* mat, render_context* rctx) override; + virtual void Disp(const mat4* mat, render_context* rctx) override; virtual void Field_40() override; virtual void Field_48() override; virtual void Field_50() override; @@ -2095,10 +2101,10 @@ public: virtual void Init() override; virtual void Field_10() override; - virtual void Field_18(int32_t stage, bool a3) override; + virtual void Field_18(int32_t stage, bool disable_external_force) override; virtual void Field_20() override; virtual void SetOsagePlayData() override; - virtual void Disp(mat4* mat, render_context* rctx) override; + virtual void Disp(const mat4* mat, render_context* rctx) override; virtual void Field_40() override; virtual void Field_48() override; virtual void Field_50() override; @@ -2156,7 +2162,7 @@ struct rob_chara_item_equip_object { void add_motion_reset_data(int32_t motion_id, float_t frame, int32_t osage_iterations); void check_no_opd(std::vector& opd_blend_data); void clear_ex_data(); - void disp(mat4* mat, render_context* rctx); + void disp(const mat4* mat, render_context* rctx); int32_t get_bone_index(const char* name, bone_database* bone_data); bone_node* get_bone_node(int32_t bone_index); bone_node* get_bone_node(const char* name, bone_database* bone_data); @@ -2174,12 +2180,15 @@ struct rob_chara_item_equip_object { void set_collision_target_osage(skin_param_osage_root& skp_root, skin_param* skp); void set_motion_reset_data(int32_t motion_id, float_t frame); void set_motion_skin_param(int8_t chara_id, int32_t motion_id, int32_t frame); - void set_null_blocks_expression_data(vec3* position, vec3* rotation, vec3* scale); + void set_null_blocks_expression_data(const vec3& position, const vec3& rotation, const vec3& scale); void set_osage_play_data_init(float_t* opdi_data); void set_osage_reset(); void set_osage_move_cancel(float_t value); void set_texture_pattern(texture_pattern_struct* tex_pat, size_t count); void skp_load(void* kv, bone_database* bone_data); + void skp_load(skin_param_osage_root& skp_root, std::vector& vec, + skin_param_file_data* skp_file_data, bone_database* bone_data); + bool skp_load_boc(skin_param_osage_root& skp_root, std::vector* node_data); void skp_load_file(void* data, bone_database* bone_data, object_database* obj_db); bool skp_load_normal_ref(skin_param_osage_root& skp_root, std::vector* node_data); }; @@ -2201,7 +2210,7 @@ struct rob_chara_item_equip { int32_t field_D4; bool disable_update; int32_t field_DC; - vec4 texture_color_coeff; + vec4 texture_color_coefficients; float_t wet; float_t wind_strength; bool chara_color; @@ -2242,10 +2251,35 @@ struct rob_chara_item_equip { rob_chara_item_equip(); ~rob_chara_item_equip(); + void add_motion_reset_data(int32_t motion_id, float_t frame, int32_t iterations); + void disp(int32_t chara_id, render_context* rctx); rob_chara_item_equip_object* get_item_equip_object(item_id id); + object_info get_object_info(item_id id); + void get_parent_bone_nodes(bone_node* bone_nodes, bone_database* bone_data); + void load_object_info(object_info obj_info, item_id id, + bool osage_reset, bone_database* bone_data, void* data, object_database* obj_db); + void load_outfit_object_info(item_id id, object_info obj_info, + bool osage_reset, bone_database* bone_data, void* data, object_database* obj_db); void reset(); void reset_external_force(); + void reset_init_data(bone_node* bone_nodes); + void set_alpha_draw_task_flags(float_t alpha, draw_task_flags flags); + void set_disp(item_id id, bool value); + void set_item_equip_range(bool value); + void set_motion_reset_data(int32_t motion_id, float_t frame); + void set_motion_skin_param(int8_t chara_id, int32_t motion_id, int32_t frame); + void set_null_blocks_expression_data(item_id id, + const vec3& position, const vec3& rotation, const vec3& scale); + void set_object_texture_pattern(item_id id, texture_pattern_struct* tex_pat, size_t count); + void set_opd_blend_data(std::list* a2); void set_osage_play_data_init(item_id id, float_t* opdi_data); + void set_osage_reset(); + void set_osage_move_cancel(uint8_t id, float_t value); + void set_shadow_type(int32_t chara_id); + void set_step(float_t value); + void set_texture_pattern(texture_pattern_struct* tex_pat, size_t count); + void skp_load(item_id id, skin_param_osage_root& skp_root, + std::vector& vec, skin_param_file_data* skp_file_data, bone_database* bone_data); }; struct item_cos_texture_change_tex { @@ -2271,6 +2305,26 @@ struct rob_chara_item_cos_data { rob_chara_item_cos_data(); ~rob_chara_item_cos_data(); + + bool check_for_npr_flag(); + const item_cos_data* get_cos(); + object_info get_head_object_replace(int32_t head_object_id); + float_t get_max_face_depth(); + void reload_items(int32_t chara_id, + bone_database* bone_data, void* data, object_database* obj_db); + void set_chara_index(::chara_index chara_index); + void set_chara_index_item(::chara_index chara_index, int32_t item_no); + void set_chara_index_item_nos(::chara_index chara_index, const int32_t* items); + void set_chara_index_item_zero(::chara_index chara_index, int32_t item_no); + void set_item(int32_t item_no); + void set_item_array(rob_chara_pv_data_item* item); + void set_item_no( item_sub_id sub_id, int32_t item_no); + void set_item_nos(const int32_t* item_nos); + void set_item_zero(int32_t item_no); + void set_texture_pattern(rob_chara_item_equip* rob_itm_equip, + uint32_t item_no, item_id id, bool tex_pat_for_all); + void texture_change_clear(); + void texture_pattern_clear(); }; struct struc_525 { @@ -3358,6 +3412,8 @@ struct rob_chara { bool is_visible(); void load_motion(int32_t motion_id, bool a3, float_t frame, MotionBlendType blend_type, bone_database* bone_data, motion_database* mot_db); + void load_outfit_object_info(item_id id, object_info obj_info, + bool osage_reset, bone_database* bone_data, void* data, object_database* obj_db); void reset_data(rob_chara_pv_data* pv_data, bone_database* bone_data, motion_database* mot_db); void reset_osage(); @@ -3394,8 +3450,9 @@ struct rob_chara { float_t step, int32_t a9, float_t blend_offset, motion_database* mot_db); void set_osage_move_cancel(uint8_t id, float_t value); void set_osage_reset(); - void set_osage_step(float_t value); void set_parts_disp(item_id id, bool disp); + void set_step(float_t step); + void set_step_motion_step(float_t value); void set_visibility(bool value); }; @@ -3404,6 +3461,18 @@ struct pv_data_set_motion { std::pair frame_stage_index; }; +struct osage_init_data { + rob_chara* rob_chr; + int32_t pv_id; + int32_t motion_id; + std::string path; + int32_t frame; + + osage_init_data(); + osage_init_data(rob_chara* rob_chr, int32_t motion_id); + ~osage_init_data(); +}; + #define ROB_CHARA_COUNT 6 extern rob_chara* rob_chara_array; @@ -3415,14 +3484,14 @@ extern int32_t chara_init_data_get_chara_size_index(chara_index chara_index); extern float_t chara_pos_adjust_y_table_get_value(uint32_t index); extern float_t chara_size_table_get_value(uint32_t index); +extern bool check_module_index_is_501(int32_t module_index); + extern const float_t get_osage_gravity_const(); extern void motion_set_load_mothead(uint32_t set, std::string* mdata_dir, motion_database* mot_db); -extern void motion_set_load_motion(uint32_t set, std::string* mdata_dir, motion_database* mot_db); extern void motion_set_unload_mothead(uint32_t set); -extern void motion_set_unload_motion(uint32_t set); -extern bool opd_make_manager_task_free(); +extern bool opd_make_manager_del_task(); extern void pv_osage_manager_array_reset(int32_t chara_id); @@ -3435,10 +3504,12 @@ extern bool rob_chara_array_check_visibility(int32_t chara_id); extern rob_chara* rob_chara_array_get(int32_t chara_id); extern rob_chara_bone_data* rob_chara_array_get_bone_data(int32_t chara_id); extern float_t rob_chara_array_get_data_adjust_scale(int32_t chara_id); +extern rob_chara_item_cos_data* rob_chara_array_get_item_cos_data(int32_t chara_id); extern rob_chara_item_equip* rob_chara_array_get_item_equip(int32_t chara_id); extern int32_t rob_chara_array_init_chara_index(chara_index chara_index, rob_chara_pv_data* pv_data, int32_t module_index, bool can_set_default); extern void rob_chara_array_free_chara_id(int32_t chara_id); +extern void rob_chara_array_reset_bone_data_item_equip(int32_t chara_id); extern void rob_chara_array_set_alpha_draw_task_flags(int32_t chara_id, float_t alpha, draw_task_flags flags); extern bool rob_chara_pv_data_array_check_chara_id(int32_t chara_id); @@ -3449,30 +3520,26 @@ extern void pv_osage_manager_array_set_pv_id(int32_t chara_id, int32_t pv_id, bo extern void pv_osage_manager_array_set_pv_set_motion( int32_t chara_id, std::vector& set_motion); -extern bool task_rob_manager_append_task(); +extern bool task_rob_load_add_task(); +extern bool task_rob_load_append_free_req_data(chara_index chara_index); +extern bool task_rob_load_append_free_req_data_obj(chara_index chara_index, const item_cos_data* cos); +extern bool task_rob_load_append_load_req_data(chara_index chara_index); +extern bool task_rob_load_append_load_req_data_obj(chara_index chara_index, const item_cos_data* cos); +extern bool task_rob_load_check_load_req_data(); +extern bool task_rob_load_del_task(); + +extern bool task_rob_manager_add_task(); extern bool task_rob_manager_check_chara_loaded(int32_t chara_id); extern bool task_rob_manager_check_task_ready(); -extern bool task_rob_manager_free_task(); - -extern void motion_storage_init(); -extern bool motion_storage_check_mot_file_not_ready(uint32_t set_id); -extern const mot_data* motion_storage_get_mot_data(uint32_t motion_id, motion_database* mot_db); -extern float_t motion_storage_get_mot_data_frame_count(uint32_t motion_id, motion_database* mot_db); -extern const mot_set* motion_storage_get_motion_set(uint32_t set_id); -extern void motion_storage_free(); +extern bool task_rob_manager_hide_task(); +extern bool task_rob_manager_run_task(); +extern bool task_rob_manager_del_task(); extern void mothead_storage_init(); extern bool mothead_storage_check_mhd_file_not_ready(uint32_t set_id); extern const mothead_mot* mothead_storage_get_mot_by_motion_id(uint32_t motion_id, motion_database* mot_db); extern void mothead_storage_free(); -extern void osage_setting_data_init(); -extern bool osage_setting_data_load_file(void* data, const char* path, const char* file, uint32_t hash); -extern bool osage_setting_data_obj_has_key(object_info key); -extern void osage_setting_data_free(); - -extern bool skin_param_manager_array_check_task_ready(); - extern void skin_param_osage_node_parse(void* kv, const char* name, std::vector* a3, skin_param_osage_root& skp_root); diff --git a/src/CRE/rob/skin_param.cpp b/src/CRE/rob/skin_param.cpp new file mode 100644 index 00000000..958f57c0 --- /dev/null +++ b/src/CRE/rob/skin_param.cpp @@ -0,0 +1,1218 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#include "rob.hpp" +#include "../../KKdLib/key_val.hpp" +#include "../../KKdLib/str_utils.hpp" +#include "../mdata_manager.hpp" +#include "skin_param.hpp" + +struct osage_setting { + std::map, osage_setting_osg_cat> cat; + std::map obj; + + osage_setting(); + ~osage_setting(); + + void clear(); + const osage_setting_osg_cat* get_cat_value( + object_info* obj_info, const char* root_node); + void load_file(); + void parse(key_val* kv); + rob_osage_parts parse_parts_string(std::string& s); +}; + +struct skin_param_file { + p_file_handler* file_handler; + item_id id; + int32_t type; + std::vector* data; + rob_chara* rob_chr; + + skin_param_file(); + ~skin_param_file(); +}; + +struct SkinParamManager : public app::Task { + int32_t state; + uint8_t index; + std::vector osage_init; + std::map>, + std::vector*> object_motion_frame; + std::map>, + std::vector*> object_motion; + std::map>, + std::vector*> object; + std::list files; + + SkinParamManager(); + virtual ~SkinParamManager() override; + + virtual bool Init() override; + virtual bool Ctrl() override; + virtual bool Dest() override; + virtual void Disp() override; + + void AddFiles(); + bool AddTask(std::vector& vec); + bool CtrlFiles(); + int32_t GetExtSkpFile(const osage_init_data& osage_init, const std::string& path, + std::string& file, void* data, motion_database* mot_db, object_database* obj_db); + std::vector* GetSkinParamFileData( + object_info obj_info, int32_t motion_id, int32_t frame); + void Reset(); +}; + +struct sp_skp_db { + std::map>> pv; + std::map>> motion; + std::vector farc_list; + size_t farc_list_count; + + sp_skp_db(); + ~sp_skp_db(); + + void clear(); + void load_file(); + void parse(key_val* kv); + + int32_t get_ext_skp_file(const osage_init_data& osage_init, + const object_info obj_info, std::string& file, std::string& farc, + void* data, motion_database* mot_db, object_database* obj_db); +}; + +static bool osage_setting_data_load_file(void* data, const char* path, const char* file, uint32_t hash); + +static SkinParamManager* skin_param_manager_get(int32_t chara_id); + +static bool sp_skp_db_load_file(void* data, const char* path, const char* file, uint32_t hash); + +osage_setting osage_setting_data; +sp_skp_db sp_skp_db_data; + +SkinParamManager* skin_param_manager; + +SkinParam::CollisionParam::CollisionParam() : type(), node_idx(), pos() { + radius = 0.2f; +} + +SkinParam::CollisionParam::~CollisionParam() { + +} + +skin_param_osage_root_normal_ref::skin_param_osage_root_normal_ref() { + +} + +skin_param_osage_root_normal_ref::~skin_param_osage_root_normal_ref() { + +} + +skin_param_osage_root_boc::skin_param_osage_root_boc() : ed_node(), st_node() { + +} + +skin_param_osage_root_boc::~skin_param_osage_root_boc() { + +} + +skin_param_osage_root::skin_param_osage_root() : field_0(), force(), force_gain(), +rot_y(), rot_z(), init_rot_y(), init_rot_z(), coli_r(), yz_order(), coli_type(), stiffness() { + air_res = 0.5f; + hinge_y = 90.0f; + hinge_z = 90.0f; + name ="NO-PARAM"; + friction = 1.0f; + wind_afc = 0.5f; + move_cancel = -0.01f; + coli.resize(13); +} + +skin_param_osage_root::~skin_param_osage_root() { + +} + +skin_param::skin_param() : friction(), wind_afc(), air_res(), rot(), init_rot(), +coli_type(), stiffness(), move_cancel(), coli_r(), force(), force_gain(), colli_tgt_osg() { + reset(); +} + +skin_param::~skin_param() { + +} + +void skin_param::set_skin_param_osage_root(skin_param_osage_root& skp_root) { + coli.assign(skp_root.coli.begin(), skp_root.coli.end()); + friction = skp_root.friction; + wind_afc = skp_root.wind_afc; + air_res = skp_root.air_res; + rot.y = skp_root.rot_y; + rot.z = skp_root.rot_z; + init_rot.y = skp_root.init_rot_y * DEG_TO_RAD_FLOAT; + init_rot.z = skp_root.init_rot_z * DEG_TO_RAD_FLOAT; + coli_type = skp_root.coli_type; + stiffness = skp_root.stiffness; + move_cancel = skp_root.move_cancel; + coli_r = skp_root.coli_r; + hinge.ymin = -skp_root.hinge_y; + hinge.ymax = skp_root.hinge_y; + hinge.zmin = -skp_root.hinge_z; + hinge.zmax = skp_root.hinge_z; + hinge.limit(); + force = skp_root.force; + force_gain = skp_root.force_gain; +} + +skin_param_file_data::skin_param_file_data() : field_88() { + +} + +skin_param_file_data::~skin_param_file_data() { + +} + +osage_setting_osg_cat::osage_setting_osg_cat() : exf() { + parts = ROB_OSAGE_PARTS_NONE; +} + +const osage_setting_osg_cat* osage_setting_data_get_cat_value( + object_info* obj_info, const char* root_node) { + return osage_setting_data.get_cat_value(obj_info, root_node); +} + +bool osage_setting_data_obj_has_key(object_info key) { + return osage_setting_data.obj.find(key) != osage_setting_data.obj.end(); +} + +void skin_param_data_init() { + osage_setting_data = {}; + sp_skp_db_data = {}; + + if (!skin_param_manager) + skin_param_manager = new SkinParamManager[ROB_CHARA_COUNT]; +} + +void skin_param_data_load() { + osage_setting_data.load_file(); + sp_skp_db_data.load_file(); +} + +void skin_param_data_free() { + if (skin_param_manager) { + delete[] skin_param_manager; + skin_param_manager = 0; + } + + sp_skp_db_data.clear(); + osage_setting_data.clear(); +} + +bool skin_param_manager_array_check_task_ready() { + bool ret = false; + for (int32_t i = 0; i < ROB_CHARA_COUNT; i++) + ret |= app::TaskWork::CheckTaskReady(&skin_param_manager[i]); + return ret; +} + +bool skin_param_manager_add_task(int32_t chara_id, std::vector& vec) { + return skin_param_manager[chara_id].AddTask(vec); +} + +bool skin_param_manager_check_task_ready(int32_t chara_id) { + return app::TaskWork::CheckTaskReady(skin_param_manager_get(chara_id)); +} + +std::vector* skin_param_manager_get_skin_param_file_data( + int32_t chara_id, object_info obj_info, int32_t motion_id, int32_t frame) { + return skin_param_manager_get(chara_id)->GetSkinParamFileData(obj_info, motion_id, frame); +} + +void skin_param_manager_reset(int32_t chara_id) { + skin_param_manager_get(chara_id)->Reset(); +} + +void skin_param_osage_node_parse(void* kv, const char* name, + std::vector* vec, skin_param_osage_root& skp_root) { + key_val* _kv = (key_val*)kv; + if (!_kv->open_scope(name)) + return; + + if (!_kv->open_scope("node")) { + _kv->close_scope(); + return; + } + + int32_t count; + if (_kv->read("length", count)) { + size_t c = vec->size(); + if (c < count) + vec->resize(count); + } + else + vec->clear(); + + int32_t j = 0; + for (auto i = vec->begin(); i != vec->end(); i++, j++) { + if (!_kv->open_scope_fmt(j)) + continue; + + *i = skin_param_osage_node(); + + float_t coli_r = 0.0f; + if (_kv->read("coli_r", coli_r)) + i->coli_r = coli_r; + + float_t weight = 0.0f; + if (_kv->read("weight", weight)) + i->weight = weight; + + float_t inertial_cancel = 0.0f; + if (_kv->read("inertial_cancel", inertial_cancel)) + i->inertial_cancel = inertial_cancel; + + i->hinge.ymin = -skp_root.hinge_y; + i->hinge.ymax = skp_root.hinge_y; + i->hinge.zmin = -skp_root.hinge_z; + i->hinge.zmax = skp_root.hinge_z; + + float_t hinge_ymax = 0.0f; + if (_kv->read("hinge_ymax", hinge_ymax)) + i->hinge.ymax = hinge_ymax; + + float_t hinge_ymin = 0.0f; + if (_kv->read("hinge_ymin", hinge_ymin)) + i->hinge.ymin = hinge_ymin; + + float_t hinge_zmax = 0.0f; + if (_kv->read("hinge_zmax", hinge_zmax)) + i->hinge.zmax = hinge_zmax; + + float_t hinge_zmin = 0.0f; + if (_kv->read("hinge_zmin", hinge_zmin)) + i->hinge.zmin = hinge_zmin; + _kv->close_scope(); + } + + _kv->close_scope(); + _kv->close_scope(); +} + +void skin_param_osage_root_parse(void* kv, const char* name, + skin_param_osage_root& skp_root, bone_database* bone_data) { + key_val* _kv = (key_val*)kv; + if (!_kv->open_scope(name)) + return; + + int32_t node_length = 0; + _kv->read("node.length", node_length); + + if (!_kv->open_scope("root")) { + _kv->close_scope(); + return; + } + + float_t force = 0.0f; + float_t force_gain = 0.0f; + if (_kv->read("force", force) + && _kv->read("force_gain", force_gain)) { + skp_root.force = force; + skp_root.force_gain = force_gain; + } + + float_t air_res = 0.0f; + if (_kv->read("air_res", air_res)) + skp_root.air_res = min_def(air_res, 0.9f); + + float_t rot_y = 0.0f; + float_t rot_z = 0.0f; + if (_kv->read("rot_y", rot_y) + && _kv->read("rot_z", rot_z)) { + skp_root.rot_y = rot_y; + skp_root.rot_z = rot_z; + } + + float_t friction = 0.0f; + if (_kv->read("friction", friction)) + skp_root.friction = friction; + + float_t wind_afc = 0; + if (_kv->read("wind_afc", wind_afc)) { + skp_root.wind_afc = wind_afc; + if (!str_utils_compare_length(name, utf8_length(name), "c_opa", 5)) + skp_root.wind_afc = 0.0f; + } + + int32_t coli_type = 0; + if (_kv->read("coli_type", coli_type)) + skp_root.coli_type = coli_type; + + float_t init_rot_y = 0.0f; + float_t init_rot_z = 0.0f; + if (_kv->read("init_rot_y", init_rot_y) + && _kv->read("init_rot_z", init_rot_z)) { + skp_root.init_rot_y = init_rot_y; + skp_root.init_rot_z = init_rot_z; + } + + float_t hinge_y = 0.0f; + float_t hinge_z = 0.0f; + if (_kv->read("hinge_y", hinge_y) + && _kv->read("hinge_z", hinge_z)) { + skp_root.hinge_y = hinge_y; + skp_root.hinge_z = hinge_z; + } + + float_t coli_r = 0.0f; + if (_kv->read("coli_r", coli_r)) + skp_root.coli_r = coli_r; + + float_t stiffness = 0.0f; + if (_kv->read("stiffness", stiffness)) + skp_root.stiffness = stiffness; + + float_t move_cancel = 0.0f; + if (_kv->read("move_cancel", move_cancel)) + skp_root.move_cancel = move_cancel; + + int32_t count; + if (_kv->read("coli", "length", count)) { + std::vector& vc = skp_root.coli; + for (int32_t i = 0; i < count; i++) { + if (!_kv->open_scope_fmt(i)) + continue; + + SkinParam::CollisionParam* c = &vc[i]; + + int32_t type = 0; + if (!_kv->read("type", type)) { + _kv->close_scope(); + break; + } + c->type = (SkinParam::CollisionType)type; + + float_t radius = 0; + if (!_kv->read("radius", radius)) { + _kv->close_scope(); + break; + } + c->radius = radius; + + const char* bone0_name; + if (!_kv->read("bone.0.name", bone0_name)) { + _kv->close_scope(); + break; + } + + c->node_idx[0] = bone_data->get_skeleton_object_bone_index( + bone_database_skeleton_type_to_string(BONE_DATABASE_SKELETON_COMMON), bone0_name); + + vec3 bone0_pos = 0.0f; + if (!_kv->read("bone.0.posx", bone0_pos.x) + || !_kv->read("bone.0.posy", bone0_pos.y) + || !_kv->read("bone.0.posz", bone0_pos.z)) { + _kv->close_scope(); + break; + } + + c->pos[0] = bone0_pos; + + const char* bone1_name; + if (_kv->read("bone.1.name", bone1_name)) { + c->node_idx[1] = bone_data->get_skeleton_object_bone_index( + bone_database_skeleton_type_to_string(BONE_DATABASE_SKELETON_COMMON), bone1_name); + + vec3 bone1_pos = 0.0f; + if (!_kv->read("bone.1.posx", bone1_pos.x) + || !_kv->read("bone.1.posy", bone1_pos.y) + || !_kv->read("bone.1.posz", bone1_pos.z)) { + _kv->close_scope(); + break; + } + + c->pos[1] = bone1_pos; + } + _kv->close_scope(); + } + _kv->close_scope(); + } + + if (_kv->read("boc", "length", count)) { + std::vector* vb = &skp_root.boc; + + vb->resize(count); + for (int32_t i = 0; i < count; i++) { + if (!_kv->open_scope_fmt(i)) + continue; + + int32_t st_node = 0; + std::string ed_root; + int32_t ed_node = 0; + if (_kv->read("st_node", st_node) + && st_node < node_length + && _kv->read("ed_root", ed_root) + && _kv->read("ed_node", ed_node) + && ed_node < node_length) { + skin_param_osage_root_boc b; + b.st_node = st_node; + b.ed_root.assign(ed_root); + b.ed_node = ed_node; + vb->push_back(b); + } + _kv->close_scope(); + } + _kv->close_scope(); + } + + std::string colli_tgt_osg; + if (_kv->read("colli_tgt_osg", colli_tgt_osg)) + skp_root.colli_tgt_osg.assign(colli_tgt_osg); + + if (_kv->read("normal_ref", "length", count)) { + std::vector* vnr = &skp_root.normal_ref; + + vnr->resize(count); + for (int32_t i = 0; i < count; i++) { + if (!_kv->open_scope_fmt(i)) + continue; + + std::string n; + if (_kv->read("N", n)) { + skin_param_osage_root_normal_ref nr; + nr.n.assign(n); + _kv->read("U", nr.u); + _kv->read("D", nr.d); + _kv->read("L", nr.l); + _kv->read("R", nr.r); + vnr->push_back(nr); + } + _kv->close_scope(); + } + _kv->close_scope(); + } + + _kv->close_scope(); + _kv->close_scope(); +} + +osage_setting::osage_setting() { + +} + +osage_setting::~osage_setting() { + +} + +void osage_setting::clear() { + cat.clear(); + obj.clear(); +} + +const osage_setting_osg_cat* osage_setting::get_cat_value( + object_info* obj_info, const char* root_node) { + auto elem_obj = osage_setting_data.obj.find(*obj_info); + if (elem_obj == osage_setting_data.obj.end()) + return 0; + + auto elem_cat = osage_setting_data.cat.find({ elem_obj->second, root_node }); + if (elem_cat == osage_setting_data.cat.end()) + return 0; + + return &elem_cat->second; +} + +void osage_setting::load_file() { + clear(); + + data_struct* aft_data = &data_list[DATA_AFT]; + for (const std::string& i : mdata_manager_get()->GetPrefixes()) { + std::string file; + file.assign(i); + file.append("osage_setting.txt"); + aft_data->load_file(this, "rom/skin_param/", + file.c_str(), osage_setting_data_load_file); + } +} + +void osage_setting::parse(key_val* kv) { + int32_t count; + if (kv->read("cat", "length", count)) { + for (int32_t i = 0; i < count; i++) { + if (!kv->open_scope_fmt(i)) + continue; + + std::string name; + kv->read("name", name); + + int32_t count; + if (kv->read("osg", "length", count)) { + for (int32_t j = 0; j < count; j++) { + if (!kv->open_scope_fmt(j)) + continue; + + int32_t exf = 0; + kv->read("exf", exf); + + std::string parts; + kv->read("parts", parts); + + std::string root; + kv->read("root", root); + + std::pair key = { name, root }; + auto elem = cat.find(key); + if (elem == cat.end()) + elem = cat.insert({ key, {} }).first; + + elem->second.exf = exf; + elem->second.parts = parse_parts_string(parts); + kv->close_scope(); + } + kv->close_scope(); + } + kv->close_scope(); + } + kv->close_scope(); + } + + data_struct* aft_data = &data_list[DATA_AFT]; + object_database* aft_obj_db = &aft_data->data_ft.obj_db; + + if (kv->read("obj", "length", count)) { + for (int32_t i = 0; i < count; i++) { + if (!kv->open_scope_fmt(i)) + continue; + + std::string cat; + kv->read("cat", cat); + + std::string name; + kv->read("name", name); + + obj.insert_or_assign(aft_obj_db->get_object_info(name.c_str()), cat); + kv->close_scope(); + } + kv->close_scope(); + } +} + +rob_osage_parts osage_setting::parse_parts_string(std::string& s) { + static const char* parts_string_array[] = { + "LEFT", "RIGHT", "CENTER", "LONG_C", "SHORT_L", "SHORT_R", + "APPEND_L", "APPEND_R", "MUFFLER", "WHITE_ONE_L", "PONY", "ANGEL_L", "ANGEL_R", + }; + + for (const char*& i : parts_string_array) + if (!s.compare(i)) + return (rob_osage_parts)(&i - parts_string_array); + return ROB_OSAGE_PARTS_NONE; +} + +skin_param_file::skin_param_file() : file_handler(), id(), type(), data(), rob_chr() { + +} + +skin_param_file::~skin_param_file() { + if (file_handler) + delete file_handler; +} + +static uint8_t skin_param_manager_index = 0; + +SkinParamManager::SkinParamManager() { + index = skin_param_manager_index++; + state = 1; +} + +SkinParamManager::~SkinParamManager() { + state = 1; + Reset(); +} + +bool SkinParamManager::Init() { + state = 0; + AddFiles(); + return true; +} + +bool SkinParamManager::Ctrl() { + if (state == 0) { + if (CtrlFiles()) + return false; + state = 1; + } + else if (state != 1) + return false; + return true; +} + +bool SkinParamManager::Dest() { + return true; +} + +void SkinParamManager::Disp() { + +} + +void SkinParamManager::AddFiles() { + if (!osage_init.size()) + return; + + data_struct* aft_data = &data_list[DATA_AFT]; + motion_database* aft_mot_db = &aft_data->data_ft.mot_db; + object_database* aft_obj_db = &aft_data->data_ft.obj_db; + + files.clear(); + for (osage_init_data& i : osage_init) { + rob_chara_item_equip* rob_itm_equip = i.rob_chr->item_equip; + for (int32_t j = ITEM_KAMI; j < ITEM_MAX; j++) { + object_info obj_info = rob_itm_equip->get_object_info((item_id)j); + if (obj_info.is_null()) + continue; + + const char* obj_name = aft_obj_db->get_object_name(obj_info); + if (!obj_name) + continue; + + obj_skin* skin = object_storage_get_obj_skin(obj_info); + if (!skin || !skin->ex_data) + return; + + obj_skin_ex_data* ex_data = skin->ex_data; + int32_t osage_count = 0; + int32_t cloth_count = 0; + + obj_skin_block* b = ex_data->block_array; + for (int32_t k = ex_data->num_block; k; k--, b++) + if (b->type == OBJ_SKIN_BLOCK_OSAGE) + osage_count++; + else if (b->type == OBJ_SKIN_BLOCK_CLOTH) + cloth_count++; + + if (!ex_data || !(osage_count + cloth_count)) + continue; + + std::string path; + std::string farc_file; + std::string file; + + path.assign("rom/skin_param/"); + + farc_file.assign(""); + + file.assign("ext_skp_"); + file.append(obj_name); + file.append(".txt"); + + skin_param_file* skp_file = new skin_param_file; + if (!skp_file) + continue; + + skp_file->id = (item_id)j; + skp_file->rob_chr = i.rob_chr; + skp_file->type = -1; + + if (i.path.size()) { + skp_file->type = GetExtSkpFile(i, i.path, + file, aft_data, aft_mot_db, aft_obj_db); + if (skp_file->type != -1) + path.assign(i.path); + } + + bool read_farc = false; + if (skp_file->type == -1) { + skp_file->type = sp_skp_db_data.get_ext_skp_file(i, + obj_info, file, farc_file, aft_data, aft_mot_db, aft_obj_db); + if (skp_file->type == -1) { + delete skp_file; + continue; + } + + switch (skp_file->type) { + case 0: + case 1: + read_farc = true; + break; + } + } + + std::vector* skp_file_data = 0; + switch (skp_file->type) { + case 0: { + std::pair> v49 = { obj_info, { i.motion_id, i.frame } }; + auto elem = object_motion_frame.find(v49); + if (elem != object_motion_frame.end()) { + delete skp_file; + continue; + } + + skp_file_data = new std::vector; + object_motion_frame.insert({ v49, skp_file_data }); + } break; + case 1: { + std::pair> v49 = { obj_info, { i.motion_id, -1 } }; + auto elem = object_motion.find(v49); + if (elem != object_motion.end()) { + delete skp_file; + continue; + } + + skp_file_data = new std::vector; + object_motion.insert({ v49, skp_file_data }); + } break; + case 2: { + std::pair> v49 = { obj_info, { -1, -1 } }; + auto elem = object.find(v49); + if (elem != object.end()) { + delete skp_file; + continue; + } + + skp_file_data = new std::vector; + object.insert({ v49, skp_file_data }); + } break; + } + + if (!skp_file_data) { + delete skp_file; + continue; + } + + skp_file_data->resize((size_t)cloth_count + osage_count); + skp_file->data = skp_file_data; + + data_struct* aft_data = &data_list[DATA_AFT]; + + p_file_handler* pfhndl = new p_file_handler; + if (read_farc) + pfhndl->read_file(aft_data, path.c_str(), farc_file.c_str(), file.c_str(), false); + else + pfhndl->read_file(aft_data, path.c_str(), file.c_str()); + skp_file->file_handler = pfhndl; + files.push_back(skp_file); + } + } +} + +bool SkinParamManager::AddTask(std::vector& vec) { + if (app::TaskWork::CheckTaskReady(this) || !app::TaskWork::AddTask(this, "SKIN_PARAM_MANAGER")) + return false; + + osage_init.assign(vec.begin(), vec.end()); + return true; +} + +bool SkinParamManager::CtrlFiles() { + data_struct* aft_data = &data_list[DATA_AFT]; + bone_database* aft_bone_data = &aft_data->data_ft.bone_data; + + for (auto i = files.begin(); i != files.end(); ) { + skin_param_file* skp_file = *i; + if (skp_file->file_handler->check_not_ready()) + break; + else if (skp_file->id <= ITEM_NONE || skp_file->id >= ITEM_MAX) { + i = files.erase(i); + continue; + } + + rob_chara_item_equip* rob_itm_equip = skp_file->rob_chr->item_equip; + + key_val kv; + kv.parse(skp_file->file_handler->get_data(), skp_file->file_handler->get_size()); + + object_info obj_info = rob_itm_equip->get_object_info(skp_file->id); + if (!obj_info.not_null()) { + i = files.erase(i); + continue; + } + + rob_chara_item_equip_object* itm_eq_obj = &rob_itm_equip->item_equip_object[skp_file->id]; + + skin_param_file_data* skp_file_data = skp_file->data->data(); + for (ExNodeBlock* j : itm_eq_obj->node_blocks) { + if (j->type != EX_OSAGE && j->type != EX_CLOTH) + continue; + + const char* name = j->name; + + skin_param_osage_root root; + skin_param_osage_root_parse(&kv, name, root, aft_bone_data); + skp_file_data->skin_param.set_skin_param_osage_root(root); + std::vector vec; + skin_param_osage_node_parse(&kv, name, &vec, root); + skp_file_data->nodes_data.resize(vec.size()); + rob_itm_equip->skp_load(skp_file->id, root, vec, skp_file_data, aft_bone_data); + skp_file_data++; + } + delete skp_file; + + i = files.erase(i); + } + return !!files.size(); +} + +int32_t SkinParamManager::GetExtSkpFile(const osage_init_data& osage_init, const std::string& path, + std::string& file, void* data, motion_database* mot_db, object_database* obj_db) { + if (!path.size()) + return -1; + + char buf0[0x200]; + char buf1[0x200]; + char buf2[0x200]; + size_t buf0_size = sizeof(buf0); + size_t buf1_size = sizeof(buf1); + size_t buf2_size = sizeof(buf2); + + if (osage_init.motion_id != -1) { + const char* motion_name = mot_db->get_motion_name(osage_init.motion_id); + if (!motion_name) + return -1; + + if (osage_init.frame > -1) { + strcpy_s(buf0, buf0_size, motion_name); + sprintf_s(buf1, buf1_size, "_%d", osage_init.frame); + strcat_s(buf0, buf0_size, buf1); + motion_name = buf0; + } + + strcpy_s(buf1, buf1_size, motion_name); + strcat_s(buf1, buf1_size, "_"); + strcat_s(buf1, buf1_size, file.c_str()); + strcpy_s(buf0, buf0_size, buf1); + + for (char* i = buf0; *i; i++) { + char c = *i; + if (c >= 'A' && c <= 'Z') + c += 0x20; + *i = c; + } + + if (((data_struct*)data)->check_file_exists(path.c_str(), buf0)) { + file.assign(buf0); + return 0; + } + } + + if (osage_init.pv_id > -1) { + sprintf_s(buf1, buf1_size, "pv%03d_", osage_init.pv_id); + strcat_s(buf1, buf1_size, file.c_str()); + strcpy_s(buf0, buf0_size, buf1); + + for (char* i = buf0; *i; i++) { + char c = *i; + if (c >= 'A' && c <= 'Z') + c += 0x20; + *i = c; + } + + if (((data_struct*)data)->check_file_exists(path.c_str(), buf0)) { + file.assign(buf0); + return 1; + } + } + + if (((data_struct*)data)->check_file_exists(path.c_str(), file.c_str())) + return 2; + + return -1; +} + +std::vector* SkinParamManager::GetSkinParamFileData( + object_info obj_info, int32_t motion_id, int32_t frame) { + if (object_motion_frame.size()) { + auto elem = object_motion_frame.find({ obj_info, { motion_id, frame } }); + if (elem != object_motion_frame.end()) + return elem->second; + } + + if (object_motion.size()) { + auto elem = object_motion.find({ obj_info, { motion_id, -1 } }); + if (elem != object_motion.end()) + return elem->second; + } + + if (object.size()) { + auto elem = object.find({ obj_info, { -1, -1 } }); + if (elem != object.end()) + return elem->second; + } + return 0; +} + +void SkinParamManager::Reset() { + osage_init.clear(); + + for (auto i : object_motion_frame) { + if (!i.second) + continue; + + delete i.second; + i.second = 0; + } + object_motion_frame.clear(); + + for (auto i : object_motion) { + if (!i.second) + continue; + + delete i.second; + i.second = 0; + } + object_motion.clear(); + + for (auto i : object) { + if (!i.second) + continue; + + delete i.second; + i.second = 0; + } + object.clear(); + + for (skin_param_file*& i : files) { + if (i->file_handler) { + delete i->file_handler; + i->file_handler = 0; + } + delete i; + } + files.clear(); + + DelTask(); +} + +sp_skp_db::sp_skp_db() : farc_list_count() { + +} + +sp_skp_db::~sp_skp_db() { + +} + +void sp_skp_db::clear() { + pv.clear(); + motion.clear(); + farc_list.clear(); + farc_list.shrink_to_fit(); + farc_list_count = 0; +} + +void sp_skp_db::load_file() { + data_struct* aft_data = &data_list[DATA_AFT]; + for (const std::string& i : mdata_manager_get()->GetPrefixes()) { + std::string file; + file.assign(i); + file.append("sp_skp_db.txt"); + aft_data->load_file(this, "rom/skin_param/", + file.c_str(), sp_skp_db_load_file); + } +} + +void sp_skp_db::parse(key_val* kv) { + data_struct* aft_data = &data_list[DATA_AFT]; + object_database* aft_obj_db = &aft_data->data_ft.obj_db; + + size_t farc_list_count = this->farc_list_count; + + int32_t count; + if (kv->read("motion", "length", count)) { + for (int32_t i = 0; i < count; i++) { + if (!kv->open_scope_fmt(i)) + continue; + + std::string name; + if (!kv->read("name", name)) + continue; + + std::shared_ptr> skp + = std::make_shared< std::map>(); + + motion.insert({ name, skp }); + + int32_t count; + if (kv->read("skp", "length", count)) { + for (int32_t j = 0; j < count; j++) { + if (!kv->open_scope_fmt(j)) + continue; + + const char* obj; + int32_t farc; + if (!kv->read("obj", obj) || !kv->read("farc", farc)) + continue; + + skp->insert({ aft_obj_db->get_object_info(obj), + (uint32_t)(farc_list_count + farc) }); + kv->close_scope(); + } + kv->close_scope(); + } + kv->close_scope(); + } + kv->close_scope(); + } + + if (kv->read("pv", "length", count)) { + for (int32_t i = 0; i < count; i++) { + if (!kv->open_scope_fmt(i)) + continue; + + std::string name; + if (!kv->read("name", name) || name.size() != 5) + continue; + + std::shared_ptr> skp + = std::make_shared< std::map>(); + + int32_t pv_id = (name[2] - '0') * 100; + pv_id += (name[3] - '0') * 10; + pv_id += name[4] - '0'; + + pv.insert({ pv_id, skp }); + + int32_t count; + if (kv->read("skp", "length", count)) { + for (int32_t j = 0; j < count; j++) { + if (!kv->open_scope_fmt(j)) + continue; + + const char* obj; + int32_t farc; + if (!kv->read("obj", obj) || !kv->read("farc", farc)) + continue; + + skp->insert({ aft_obj_db->get_object_info(obj), + (uint32_t)(farc_list_count + farc) }); + kv->close_scope(); + } + kv->close_scope(); + } + kv->close_scope(); + } + kv->close_scope(); + } + + if (kv->read("farc_list", "length", count)) { + farc_list.resize(farc_list_count + count); + std::string* vfl = farc_list.data(); + for (int32_t i = 0; i < count; i++) { + if (!kv->open_scope_fmt(i)) + continue; + + kv->read(vfl[farc_list_count + i]); + kv->close_scope(); + } + kv->close_scope(); + this->farc_list_count = farc_list_count + count; + } +} + +int32_t sp_skp_db::get_ext_skp_file(const osage_init_data& osage_init, + const object_info obj_info, std::string& file, std::string& farc, + void* data, motion_database* mot_db, object_database* obj_db) { + if (obj_info.is_null()) + return 2; + + int32_t type = 2; + farc.clear(); + if (osage_init.pv_id > -1) { + auto elem0 = pv.find(osage_init.pv_id); + if (elem0 != pv.end()) { + auto elem1 = elem0->second->find(obj_info); + if (elem1 != elem0->second->end()) { + type = 1; + farc.assign(farc_list[elem1->second]); + } + } + } + + char buf0[0x200]; + char buf1[0x200]; + char buf2[0x200]; + size_t buf0_size = sizeof(buf0); + size_t buf1_size = sizeof(buf1); + size_t buf2_size = sizeof(buf2); + + if (osage_init.motion_id != -1) { + const char* motion_name = mot_db->get_motion_name(osage_init.motion_id); + if (motion_name) { + if (osage_init.frame > -1) { + strcpy_s(buf0, buf0_size, motion_name); + sprintf_s(buf1, buf1_size, "_%d", osage_init.frame); + strcat_s(buf0, buf0_size, buf1); + motion_name = buf0; + } + + auto elem0 = motion.find(motion_name); + if (elem0 != motion.end()) { + auto elem1 = elem0->second->find(obj_info); + if (elem1 != elem0->second->end()) { + type = 0; + farc.assign(farc_list[elem1->second]); + } + } + } + } + + const char* obj_name = obj_db->get_object_name(obj_info); + sprintf_s(buf0, buf0_size, "ext_skp_%s.txt", obj_name); + + if (type == 1) { + sprintf_s(buf1, buf1_size, "pv%03d_", osage_init.pv_id); + strcat_s(buf1, buf1_size, buf0); + strcpy_s(buf0, buf0_size, buf1); + } + else if (!type) { + const char* motion_name = mot_db->get_motion_name(osage_init.motion_id); + if (osage_init.frame > -1) { + strcpy_s(buf2, buf2_size, motion_name); + sprintf_s(buf1, buf1_size, "_%d", osage_init.frame); + strcat_s(buf2, buf2_size, buf1); + motion_name = buf2; + } + + strcpy_s(buf1, buf1_size, motion_name); + strcat_s(buf1, buf1_size, "_"); + strcat_s(buf1, buf1_size, buf0); + strcpy_s(buf0, buf0_size, buf1); + } + + for (char* i = buf0; *i; i++) { + char c = *i; + if (c >= 'A' && c <= 'Z') + c += 0x20; + *i = c; + } + + if (type == 2 && !((data_struct*)data)->check_file_exists("rom/skin_param/", buf0)) { + file.clear(); + type = -1; + } + else + file.assign(buf0); + return type; +} + +static bool osage_setting_data_load_file(void* data, const char* path, const char* file, uint32_t hash) { + key_val kv; + if (key_val::load_file(&kv, path, file, hash)) { + ((osage_setting*)data)->parse(&kv); + return true; + } + return false; +} + +static SkinParamManager* skin_param_manager_get(int32_t chara_id) { + if (chara_id < 0 || chara_id > 6) + chara_id = 0; + return &skin_param_manager[chara_id]; +} + +static bool sp_skp_db_load_file(void* data, const char* path, const char* file, uint32_t hash) { + key_val kv; + if (key_val::load_file(&kv, path, file, hash)) { + ((sp_skp_db*)data)->parse(&kv); + return true; + } + return false; +} diff --git a/src/CRE/rob/skin_param.hpp b/src/CRE/rob/skin_param.hpp new file mode 100644 index 00000000..ea27ba7c --- /dev/null +++ b/src/CRE/rob/skin_param.hpp @@ -0,0 +1,24 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#pragma once + +#include "rob.hpp" + +extern const osage_setting_osg_cat* osage_setting_data_get_cat_value( + object_info* obj_info, const char* root_node); +extern bool osage_setting_data_obj_has_key(object_info key); + +extern void skin_param_data_init(); +extern void skin_param_data_load(); +extern void skin_param_data_free(); + +extern bool skin_param_manager_array_check_task_ready(); + +extern bool skin_param_manager_add_task(int32_t chara_id, std::vector& vec); +extern bool skin_param_manager_check_task_ready(int32_t chara_id); +extern std::vector* skin_param_manager_get_skin_param_file_data( + int32_t chara_id, object_info obj_info, int32_t motion_id, int32_t frame); +extern void skin_param_manager_reset(int32_t chara_id); diff --git a/src/CRE/shader.cpp b/src/CRE/shader.cpp index afcc0433..7cc69e37 100644 --- a/src/CRE/shader.cpp +++ b/src/CRE/shader.cpp @@ -17,6 +17,13 @@ struct program_binary { size_t binary; }; +struct program_spv { + size_t size; + size_t spv; +}; + +extern bool vulkan_render; + static GLuint shader_compile_shader(GLenum type, const char* data, const char* file); static GLuint shader_compile(const char* vert, const char* frag, const char* vp, const char* fp); static GLuint shader_compile_binary(const char* vert, const char* frag, const char* vp, const char* fp, @@ -24,112 +31,22 @@ static GLuint shader_compile_binary(const char* vert, const char* frag, const ch static bool shader_load_binary_shader(program_binary* bin, GLuint* program); static void shader_update_data(shader_set_data* set); -shader_state_clip::shader_state_clip() { +int32_t shader_opengl::bind(shader_set_data* set, uint32_t sub_index) { + shader_opengl_data* opengl_data = set->opengl_data; + opengl_data->curr_shader = 0; -} + if (num_sub < 1) + return -1; -shader_state_depth::shader_state_depth() { - -} - -shader_state_fog::shader_state_fog() { - -} - -shader_state_light::shader_state_light() { - -} - -shader_state_lightmodel::shader_state_lightmodel() { - -} - -shader_state_lightprod::shader_state_lightprod() { - -} - -shader_state_material::shader_state_material() { - -} - -shader_state_matrix_data::shader_state_matrix_data() { - -} - -shader_state_matrix::shader_state_matrix() { - -} - -shader_state_point::shader_state_point() { - -} - -shader_state_texgen::eye::eye() { - -} - -shader_state_texgen::object::object() { - -} - -shader_state_texgen::shader_state_texgen() { - -} - -shader_state_texenv::shader_state_texenv() { - -} - -shader_buffer::shader_buffer() { - -} - -shader_env::shader_env() { - -} - -shader_state::shader_state() { - -} - -shader_data::shader_data() : local(), local_uniform(), state_update_data(), -state_matrix_update_data(), env_update_data(), buffer_update_data(), local_update_data(), -local_uniform_update_data(), state_ubo(), state_matrix_ubo(), env_ubo(), buffer_ubo() { - -} - -int32_t shader::bind(shader_set_data* set, uint32_t sub_index) { int32_t sub_shader_index = 0; - if (num_sub < 1) { - set->data.local = 0; - set->data.local_uniform = 0; - set->data.local_update_data = 0; - set->data.local_uniform_update_data = 0; - return -1; - } - - for (shader_sub* i = sub; i->sub_index != sub_index; i++) { - sub_shader_index++; - if (sub_shader_index >= num_sub) { - set->data.local = 0; - set->data.local_uniform = 0; - set->data.local_update_data = 0; - set->data.local_uniform_update_data = 0; + for (shader_opengl_sub* i = sub; i->sub_index != sub_index; i++) + if (++sub_shader_index >= num_sub) return -1; - } - } - shader_sub* sub_shader = &sub[sub_shader_index]; - if (!sub_shader) { - set->data.local = 0; - set->data.local_uniform = 0; - set->data.local_update_data = 0; - set->data.local_uniform_update_data = 0; - return -1; - } + shader_opengl_sub* sub_shader = &sub[sub_shader_index]; - int32_t unival_curr = 1; - int32_t unival = 0; + int32_t unival_shad_curr = 1; + int32_t unival_shad = 0; int32_t uniform_val[SHADER_MAX_UNIFORM_VALUES] = {}; if (num_uniform > 0) { const int32_t* vp_unival_max = sub_shader->vp_unival_max; @@ -137,34 +54,29 @@ int32_t shader::bind(shader_set_data* set, uint32_t sub_index) { int32_t i = 0; for (i = 0; i < num_uniform && i < sizeof(uniform_val) / sizeof(int32_t); i++) { - int32_t unival_max = use_permut[i] + const int32_t unival = uniform_value[use_uniform[i]]; + const int32_t unival_max = use_permut[i] ? max_def(vp_unival_max[i], fp_unival_max[i]) : 0; - unival += unival_curr * min_def(uniform_value[(int32_t)use_uniform[i]], unival_max); - unival_curr *= unival_max + 1; + unival_shad += unival_shad_curr * min_def(unival, unival_max); + unival_shad_curr *= unival_max + 1; int32_t unival_max_glsl = max_def(vp_unival_max[i], fp_unival_max[i]); - uniform_val[i] = min_def(uniform_value[use_uniform[i]], unival_max_glsl); + uniform_val[i] = min_def(unival, unival_max_glsl); } } - shader_sub_shader& shader = sub_shader->shaders[unival]; + shader_opengl_sub_shader* shader = &sub_shader->shaders[unival_shad]; + opengl_data->curr_shader = shader; - set->data.local = &shader.data; - set->data.local_uniform = &shader.data_uniform; - set->data.local_update_data = shader.data_update; - set->data.local_uniform_update_data = &shader.data_uniform_update; - - gl_state_use_program(shader.program); - if (memcmp(set->data.local_uniform->uniform_val, uniform_val, sizeof(uniform_val))) { - memcpy(set->data.local_uniform->uniform_val, uniform_val, sizeof(uniform_val)); - *set->data.local_uniform_update_data = true; + gl_state_use_program(shader->program); + if (memcmp(shader->uniform_val, uniform_val, sizeof(uniform_val))) { + memcpy(shader->uniform_val, uniform_val, sizeof(uniform_val)); + shader->uniform_val_update = true; } return 0; } -bool shader::parse_define(const char* data, int32_t num_uniform, - const int32_t* vp_unival_max, const int32_t* fp_unival_max, - int32_t* uniform_value, char** temp, size_t* temp_size) { +bool shader_opengl::parse_define(const char* data, char** temp, size_t* temp_size) { if (!data) return false; @@ -182,28 +94,46 @@ bool shader::parse_define(const char* data, int32_t num_uniform, return true; } - if (!num_uniform || !vp_unival_max || !fp_unival_max || !uniform_value) { - size_t len_a = def - data; - def += 5; - if (*def == '\n') - def++; + size_t len_a = def - data; + def += 5; + if (*def == '\n') + def++; - size_t len_b = utf8_length(def); - size_t len = 0; - len += len_a + len_b; + size_t len_b = utf8_length(def); + size_t len = 0; + len += len_a + len_b; + if (len + 1 > *temp_size) { + free_def(*temp); + *temp_size = len + 1; + *temp = force_malloc_s(char, *temp_size); + } + + size_t pos = 0; + memcpy(*temp + pos, data, len_a); + pos += len_a; + memcpy(*temp + pos, def, len_b); + pos += len_b; + (*temp)[pos] = 0; + return true; +} + +bool shader_opengl::parse_define(const char* data, int32_t num_uniform, + int32_t* uniform_value, char** temp, size_t* temp_size) { + if (!data) + return false; + + const char* def = strstr(data, "//DEF\n"); + if (!def) { + size_t len = utf8_length(data); if (len + 1 > *temp_size) { free_def(*temp); *temp_size = len + 1; *temp = force_malloc_s(char, *temp_size); } - size_t pos = 0; - memcpy(*temp + pos, data, len_a); - pos += len_a; - memcpy(*temp + pos, def, len_b); - pos += len_b; - (*temp)[pos] = 0; + memcpy(*temp, data, len); + (*temp)[len] = 0; return true; } @@ -247,7 +177,7 @@ bool shader::parse_define(const char* data, int32_t num_uniform, return true; } -char* shader::parse_include(char* data, farc* f) { +char* shader_opengl::parse_include(char* data, farc* f) { if (!data || !f) return data; @@ -358,57 +288,15 @@ char* shader::parse_include(char* data, farc* f) { return temp_data; } -void shader::unbind() { +void shader_opengl::unbind() { gl_state_use_program(0); - gl_state_bind_uniform_buffer_base(0, 0); - gl_state_bind_uniform_buffer_base(1, 0); - gl_state_bind_uniform_buffer_base(2, 0); - gl_state_bind_uniform_buffer_base(3, 0); } -shader_set_data::shader_set_data() : shaders(), - size(), data(), primitive_restart(), primitive_restart_index() { - -} - -void shader_set_data::disable_primitive_restart() { - primitive_restart = false; -} - -void shader_set_data::draw_arrays(GLenum mode, GLint first, GLsizei count) { - shader_update_data(this); - glDrawArrays(mode, first, count); -} - -void shader_set_data::draw_elements(GLenum mode, - GLsizei count, GLenum type, const void* indices) { - shader_update_data(this); - glDrawElements(mode, count, type, indices); -} - -void shader_set_data::draw_range_elements(GLenum mode, - GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices) { - shader_update_data(this); - glDrawRangeElements(mode, start, end, count, type, indices); -} - -void shader_set_data::enable_primitive_restart() { - primitive_restart = true; -} - -int32_t shader_set_data::get_index_by_name(const char* name) { - for (size_t i = 0; i < size; i++) - if (!str_utils_compare(shaders[i].name, name)) - return (int32_t)shaders[i].name_index; - return -1; -} - -void shader_set_data::load(farc* f, bool ignore_cache, +shader_opengl_data::shader_opengl_data(farc* f, bool ignore_cache, const char* name, const shader_table* shaders_table, const size_t size, - const shader_bind_func* bind_func_table, const size_t bind_func_table_size) { - if (!this || !f || !shaders_table || !size) - return; - + const shader_opengl_bind_func* bind_func_table, const size_t bind_func_table_size, + PFNSHADERGETINDEXFUNCPROC get_index_by_name) : size(), shaders(), + curr_shader(), primitive_restart(), primitive_restart_index(), get_index_by_name() { wchar_t temp_buf[MAX_PATH]; if (FAILED(SHGetFolderPathW(0, CSIDL_LOCAL_APPDATA, 0, 0, temp_buf))) return; @@ -426,7 +314,12 @@ void shader_set_data::load(farc* f, bool ignore_cache, if (!ignore_cache && path_check_file_exists(buf)) shader_cache_farc.read(buf, true, false); - GLsizei buffer_size = 0x20000; + char vert_buf[MAX_PATH]; + char frag_buf[MAX_PATH]; + char vert_file_buf[MAX_PATH]; + char frag_file_buf[MAX_PATH]; + + GLsizei buffer_size = 0x100000; void* binary = force_malloc(buffer_size); size_t temp_vert_size = 0x10000; char* temp_vert = force_malloc_s(char, temp_vert_size); @@ -435,29 +328,31 @@ void shader_set_data::load(farc* f, bool ignore_cache, std::vector vec_vert; std::vector vec_frag; std::vector program_data_binary; - shaders = force_malloc_s(shader, size); + shader_opengl* shaders = force_malloc_s(shader_opengl, size); + this->shaders = shaders; this->size = size; for (size_t i = 0; i < size; i++) { - shader* shader = &shaders[i]; + shader_opengl* shader = &shaders[i]; shader->name = shaders_table[i].name; shader->name_index = shaders_table[i].name_index; shader->num_sub = shaders_table[i].num_sub; - shader->sub = force_malloc_s(shader_sub, shader->num_sub); + shader->sub = force_malloc_s(shader_opengl_sub, shader->num_sub); shader->num_uniform = shaders_table[i].num_uniform; shader->use_uniform = shaders_table[i].use_uniform; shader->use_permut = shaders_table[i].use_permut; int32_t num_sub = shader->num_sub; const shader_sub_table* sub_table = shaders_table[i].sub; - shader_sub* sub = shader->sub; + shader_opengl_sub* sub = shader->sub; vec_vert.resize(shader->num_uniform); vec_frag.resize(shader->num_uniform); + int32_t* vec_vert_data = vec_vert.data(); + int32_t* vec_frag_data = vec_frag.data(); for (size_t j = 0; j < num_sub; j++, sub++, sub_table++) { sub->sub_index = sub_table->sub_index; sub->vp_unival_max = sub_table->vp_unival_max; sub->fp_unival_max = sub_table->fp_unival_max; - char vert_file_buf[MAX_PATH]; strcpy_s(vert_file_buf, sizeof(vert_file_buf), sub_table->vp); strcat_s(vert_file_buf, sizeof(vert_file_buf), ".vert"); farc_file* vert_ff = f->read_file(vert_file_buf); @@ -471,7 +366,6 @@ void shader_set_data::load(farc* f, bool ignore_cache, } } - char frag_file_buf[MAX_PATH]; strcpy_s(frag_file_buf, sizeof(frag_file_buf), sub_table->fp); strcat_s(frag_file_buf, sizeof(frag_file_buf), ".frag"); farc_file* frag_ff = f->read_file(frag_file_buf); @@ -509,8 +403,8 @@ void shader_set_data::load(farc* f, bool ignore_cache, } strcat_s(shader_cache_file_name, sizeof(shader_cache_file_name), ".bin"); - vert_data = shader::parse_include(vert_data, f); - frag_data = shader::parse_include(frag_data, f); + vert_data = shader_opengl::parse_include(vert_data, f); + frag_data = shader_opengl::parse_include(frag_data, f); uint64_t vert_data_hash = hash_utf8_fnv1a64m(vert_data); uint64_t frag_data_hash = hash_utf8_fnv1a64m(frag_data); @@ -526,26 +420,25 @@ void shader_set_data::load(farc* f, bool ignore_cache, bin = (program_binary*)&((uint64_t*)shader_cache_file->data)[2]; } - if (shader->num_uniform > 0) { + if (shader->num_uniform > 0 + && (sub_table->vp_unival_max[0] != -1 || sub_table->fp_unival_max[0] != -1)) { int32_t num_uniform = shader->num_uniform; - size_t unival_curr = 1; - size_t unival_count = 1; + int32_t unival_shad_curr = 1; + int32_t unival_shad_count = 1; const int32_t* vp_unival_max = sub_table->vp_unival_max; const int32_t* fp_unival_max = sub_table->fp_unival_max; for (size_t k = 0; k < num_uniform; k++) { - size_t unival_max = shader->use_permut[k] + const int32_t unival_max = shader->use_permut[k] ? max_def(vp_unival_max[k], fp_unival_max[k]) : 0; - unival_count += unival_curr * unival_max; - unival_curr *= unival_max + 1; + unival_shad_count += unival_shad_curr * unival_max; + unival_shad_curr *= unival_max + 1; } if (!ignore_cache) - program_data_binary.reserve(unival_count); - sub->shaders = force_malloc_s(shader_sub_shader, unival_count); - if (sub->shaders) { - char vert_buf[MAX_PATH]; - char frag_buf[MAX_PATH]; - + program_data_binary.reserve(unival_shad_count); + shader_opengl_sub_shader* shaders = force_malloc_s(shader_opengl_sub_shader, unival_shad_count); + sub->shaders = shaders; + if (shaders) { strcpy_s(vert_buf, sizeof(vert_buf), sub_table->vp); size_t vert_buf_pos = utf8_length(vert_buf); vert_buf[vert_buf_pos++] = '.'; @@ -562,41 +455,39 @@ void shader_set_data::load(farc* f, bool ignore_cache, frag_buf[frag_buf_pos + num_uniform] = 0; strcat_s(frag_buf, sizeof(frag_buf), ".frag"); - for (size_t k = 0; k < unival_count; k++) { + for (size_t k = 0; k < unival_shad_count; k++) { for (size_t l = 0, m = k; l < num_uniform; l++) { size_t unival_max = (size_t)(shader->use_permut[l] ? max_def(vp_unival_max[l], fp_unival_max[l]) : 0) + 1; - vec_vert[l] = (uint32_t)(min_def(m % unival_max, vp_unival_max[l])); + vec_vert_data[l] = (uint32_t)(min_def(m % unival_max, vp_unival_max[l])); m /= unival_max; - vert_buf[vert_buf_pos + l] = (char)('0' + vec_vert[l]); + vert_buf[vert_buf_pos + l] = (char)('0' + vec_vert_data[l]); } for (size_t l = 0, m = k; l < num_uniform; l++) { size_t unival_max = (size_t)(shader->use_permut[l] ? max_def(vp_unival_max[l], fp_unival_max[l]) : 0) + 1; - vec_frag[l] = (uint32_t)(min_def(m % unival_max, fp_unival_max[l])); + vec_frag_data[l] = (uint32_t)(min_def(m % unival_max, fp_unival_max[l])); m /= unival_max; - frag_buf[frag_buf_pos + l] = (char)('0' + vec_frag[l]); + frag_buf[frag_buf_pos + l] = (char)('0' + vec_frag_data[l]); } - if (!bin || !shader_load_binary_shader(bin, &sub->shaders[k].program)) { - bool vert_succ = shader::parse_define(vert_data, - num_uniform, vp_unival_max, fp_unival_max, - vec_vert.data(), &temp_vert, &temp_vert_size); - bool frag_succ = shader::parse_define(frag_data, - num_uniform, vp_unival_max, fp_unival_max, - vec_frag.data(), &temp_frag, &temp_frag_size); + if (!bin || !shader_load_binary_shader(bin, &shaders[k].program)) { + bool vert_succ = shader_opengl::parse_define(vert_data, num_uniform, + vec_vert_data, &temp_vert, &temp_vert_size); + bool frag_succ = shader_opengl::parse_define(frag_data, num_uniform, + vec_frag_data, &temp_frag, &temp_frag_size); if (ignore_cache) - sub->shaders[k].program = shader_compile(vert_succ ? temp_vert : 0, + shaders[k].program = shader_compile(vert_succ ? temp_vert : 0, frag_succ ? temp_frag : 0, vert_buf, frag_buf); else { program_data_binary.push_back({}); - sub->shaders[k].program = shader_compile_binary(vert_succ ? temp_vert : 0, + shaders[k].program = shader_compile_binary(vert_succ ? temp_vert : 0, frag_succ ? temp_frag : 0, vert_buf, frag_buf, &program_data_binary.back(), &buffer_size, &binary); } - shader_cache_changed |= sub->shaders[k].program ? true : false; + shader_cache_changed |= shaders[k].program ? true : false; } else { program_data_binary.push_back({}); @@ -614,31 +505,28 @@ void shader_set_data::load(farc* f, bool ignore_cache, } else { program_data_binary.reserve(1); - sub->shaders = force_malloc_s(shader_sub_shader, 1); - if (sub->shaders) { - char vert_buf[MAX_PATH]; - char frag_buf[MAX_PATH]; + shader_opengl_sub_shader* shaders = force_malloc_s(shader_opengl_sub_shader, 1); + sub->shaders = shaders; + if (shaders) { strcpy_s(vert_buf, sizeof(vert_buf), sub_table->vp); strcpy_s(frag_buf, sizeof(vert_buf), sub_table->fp); strcat_s(vert_buf, sizeof(vert_buf), "..vert"); strcat_s(frag_buf, sizeof(vert_buf), "..frag"); - if (!bin || !shader_load_binary_shader(bin, &sub->shaders[0].program)) { - bool vert_succ = shader::parse_define(vert_data, - 0, 0, 0, 0, &temp_vert, &temp_vert_size); - bool frag_succ = shader::parse_define(frag_data, - 0, 0, 0, 0, &temp_frag, &temp_frag_size); + if (!bin || !shader_load_binary_shader(bin, &shaders[0].program)) { + bool vert_succ = shader_opengl::parse_define(vert_data, &temp_vert, &temp_vert_size); + bool frag_succ = shader_opengl::parse_define(frag_data, &temp_frag, &temp_frag_size); if (ignore_cache) - sub->shaders[0].program = shader_compile(vert_succ ? temp_vert : 0, + shaders[0].program = shader_compile(vert_succ ? temp_vert : 0, frag_succ ? temp_frag : 0, vert_buf, frag_buf); else { program_data_binary.push_back({}); - sub->shaders[0].program = shader_compile_binary(vert_succ ? temp_vert : 0, + shaders[0].program = shader_compile_binary(vert_succ ? temp_vert : 0, frag_succ ? temp_frag : 0, vert_buf, frag_buf, &program_data_binary.back(), &buffer_size, &binary); } - shader_cache_changed |= sub->shaders[0].program ? true : false; + shader_cache_changed |= shaders[0].program ? true : false; } else { program_data_binary.push_back({}); @@ -663,7 +551,7 @@ void shader_set_data::load(farc* f, bool ignore_cache, size_t bin_count = program_data_binary.size(); size_t bin_size = sizeof(uint64_t) * 2 + bin_count * sizeof(program_binary); for (program_binary& k : program_data_binary) - bin_size += k.length; + bin_size += align_val(k.length, 0x04ULL); shader_cache_file->data = force_malloc(bin_size); shader_cache_file->size = bin_size; shader_cache_file->data_changed = true; @@ -679,7 +567,7 @@ void shader_set_data::load(farc* f, bool ignore_cache, bin->binary = bin_data - bin_data_base; memcpy((void*)bin_data, (void*)k.binary, k.length); bin_data_base += sizeof(program_binary); - bin_data += k.length; + bin_data += align_val(k.length, 0x04ULL); void* binary = (void*)k.binary; free_def(binary); k.binary = 0; @@ -707,1653 +595,455 @@ void shader_set_data::load(farc* f, bool ignore_cache, if (shader_cache_changed) shader_cache_farc.write(temp_buf, FARC_COMPRESS_FArC, false); - memset(&data, 0, sizeof(data)); - if (GLAD_GL_VERSION_4_4) { - glGenBuffers(4, &data.state_ubo); - - gl_state_bind_uniform_buffer(data.state_ubo, true); - glBufferStorage(GL_UNIFORM_BUFFER, sizeof(data.state), 0, GL_DYNAMIC_STORAGE_BIT); - - gl_state_bind_uniform_buffer(data.state_matrix_ubo, true); - glBufferStorage(GL_UNIFORM_BUFFER, sizeof(data.state_matrix), 0, GL_DYNAMIC_STORAGE_BIT); - - gl_state_bind_uniform_buffer(data.env_ubo, true); - glBufferStorage(GL_UNIFORM_BUFFER, sizeof(data.env), 0, GL_DYNAMIC_STORAGE_BIT); - - gl_state_bind_uniform_buffer(data.buffer_ubo, true); - glBufferStorage(GL_UNIFORM_BUFFER, sizeof(data.buffer), 0, GL_DYNAMIC_STORAGE_BIT); - - gl_state_bind_uniform_buffer(0); - } - else { - glGenBuffers(4, &data.state_ubo); - - gl_state_bind_uniform_buffer(data.state_ubo, true); - glBufferData(GL_UNIFORM_BUFFER, sizeof(data.state), 0, GL_DYNAMIC_DRAW); - - gl_state_bind_uniform_buffer(data.state_matrix_ubo, true); - glBufferData(GL_UNIFORM_BUFFER, sizeof(data.state_matrix), 0, GL_DYNAMIC_DRAW); - - gl_state_bind_uniform_buffer(data.env_ubo, true); - glBufferData(GL_UNIFORM_BUFFER, sizeof(data.env), 0, GL_DYNAMIC_DRAW); - - gl_state_bind_uniform_buffer(data.buffer_ubo, true); - glBufferData(GL_UNIFORM_BUFFER, sizeof(data.buffer), 0, GL_DYNAMIC_DRAW); - - gl_state_bind_uniform_buffer(0); - } + this->get_index_by_name = get_index_by_name; } -void shader_set_data::set(uint32_t index) { - if (this && index && shaders && index != -1) { - env_frag_set(0x18, (float_t)uniform_value[U_TEXTURE_BLEND], 0.0, 0.0, 0.0); - shader* shader = &shaders[index]; - if (shader->bind_func) - shader->bind_func(this, shader); - else - shader->bind(this, shader->sub[0].sub_index); - gl_state_bind_uniform_buffer_range(0, data.state_ubo, 0, sizeof(data.state)); - gl_state_bind_uniform_buffer_range(1, data.state_matrix_ubo, 0, sizeof(data.state_matrix)); - gl_state_bind_uniform_buffer_range(2, data.env_ubo, 0, sizeof(data.env)); - gl_state_bind_uniform_buffer_range(3, data.buffer_ubo, 0, sizeof(data.buffer)); - } - else - shader::unbind(); -} - -void shader_set_data::set_primitive_restart_index(GLuint index) { - if (primitive_restart_index != index) - primitive_restart_index = index; -} - -void shader_set_data::unload() { - glDeleteBuffers(4, &data.state_ubo); - memset(&data, 0, sizeof(data)); - +shader_opengl_data::~shader_opengl_data() { + size_t size = this->size; + shader_opengl* shaders = this->shaders; for (size_t i = 0; i < size; i++) { - shader* shader = &shaders[i]; + shader_opengl* shader = &shaders[i]; int32_t num_sub = shader->num_sub; - shader_sub* sub = shader->sub; + shader_opengl_sub* sub = shader->sub; for (size_t j = 0; j < num_sub; j++, sub++) { + int32_t unival_shad_count = 1; if (shader->num_uniform > 0) { int32_t num_uniform = shader->num_uniform; - size_t unival_curr = 1; - size_t unival_count = 1; + int32_t unival_shad_curr = 1; const int32_t* vp_unival_max = sub->vp_unival_max; const int32_t* fp_unival_max = sub->fp_unival_max; for (size_t k = 0; k < num_uniform; k++) { - size_t unival_max = shader->use_permut[k] + const int32_t unival_max = shader->use_permut[k] ? max_def(vp_unival_max[k], fp_unival_max[k]) : 0; - unival_count += unival_curr * unival_max; - unival_curr *= unival_max + 1; + unival_shad_count += unival_shad_curr * unival_max; + unival_shad_curr *= unival_max + 1; } - - if (sub->shaders) - for (size_t k = 0; k < unival_count; k++) - glDeleteProgram(sub->shaders[k].program); - free_def(sub->shaders); } - else { - if (sub->shaders) - glDeleteProgram(sub->shaders[0].program); - free_def(sub->shaders); + + if (sub->shaders) { + shader_opengl_sub_shader* shaders = sub->shaders; + for (size_t k = 0; k < unival_shad_count; k++) + glDeleteProgram(shaders[k].program); + free(shaders); + sub->shaders = 0; } } free_def(shader->sub); } free_def(shaders); -} - -void shader_set_data::buffer_get(size_t index, vec4& data) { - if (!this || index >= SHADER_MAX_PROGRAM_BUFFER_PARAMETERS) - return; - - data = this->data.buffer.buffer[index]; -} - -void shader_set_data::buffer_get(size_t index, size_t count, vec4* data) { - if (!this || index >= SHADER_MAX_PROGRAM_BUFFER_PARAMETERS - || index + count > SHADER_MAX_PROGRAM_BUFFER_PARAMETERS || !data) - return; - - memcpy(data, &this->data.buffer.buffer[index], sizeof(vec4) * count); -} - -void shader_set_data::local_frag_get(size_t index, float_t* x, float_t* y, float_t* z, float_t* w) { - if (!this || index >= SHADER_MAX_PROGRAM_LOCAL_PARAMETERS || !x || !y || !z || !w) - return; - - GLfloat temp[4]; - glGetUniformfv(gl_state_get_program(), (GLint)(index + SHADER_MAX_PROGRAM_LOCAL_PARAMETERS), temp); - *x = temp[0]; - *y = temp[1]; - *z = temp[2]; - *w = temp[3]; -} - -void shader_set_data::local_frag_get(size_t index, vec4& data) { - if (!this || index >= SHADER_MAX_PROGRAM_LOCAL_PARAMETERS) - return; - - GLfloat temp[4]; - glGetUniformfv(gl_state_get_program(), (GLint)(index + SHADER_MAX_PROGRAM_LOCAL_PARAMETERS), temp); - data = { temp[0], temp[1], temp[2], temp[3] }; -} - -void shader_set_data::local_vert_get(size_t index, float_t* x, float_t* y, float_t* z, float_t* w) { - if (!this || index >= SHADER_MAX_PROGRAM_LOCAL_PARAMETERS || !x || !y || !z || !w) - return; - - GLfloat temp[4]; - glGetUniformfv(gl_state_get_program(), (GLint)index, temp); - *x = temp[0]; - *y = temp[1]; - *z = temp[2]; - *w = temp[3]; -} - -void shader_set_data::local_vert_get(size_t index, vec4& data) { - if (!this || index >= SHADER_MAX_PROGRAM_LOCAL_PARAMETERS) - return; - - GLfloat temp[4]; - glGetUniformfv(gl_state_get_program(), (GLint)index, temp); - data = { temp[0], temp[1], temp[2], temp[3] }; -} - -void shader_set_data::env_frag_get(size_t index, float_t* x, float_t* y, float_t* z, float_t* w) { - if (!this || index >= SHADER_MAX_PROGRAM_ENV_PARAMETERS || !x || !y || !z || !w) - return; - - vec4& data = this->data.env.frag[index]; - *x = data.x; - *y = data.y; - *z = data.z; - *w = data.w; -} - -void shader_set_data::env_frag_get(size_t index, vec4& data) { - if (!this || index >= SHADER_MAX_PROGRAM_ENV_PARAMETERS) - return; - - data = this->data.env.frag[index]; -} - -void shader_set_data::env_frag_get(size_t index, size_t count, vec4* data) { - if (!this || index >= SHADER_MAX_PROGRAM_ENV_PARAMETERS - || index + count > SHADER_MAX_PROGRAM_ENV_PARAMETERS || !data) - return; - - memcpy(data, &this->data.env.frag[index], sizeof(vec4) * count); -} - -void shader_set_data::env_vert_get(size_t index, float_t* x, float_t* y, float_t* z, float_t* w) { - if (!this || index >= SHADER_MAX_PROGRAM_ENV_PARAMETERS || !x || !y || !z || !w) - return; - - vec4 data = this->data.env.vert[index]; - *x = data.x; - *y = data.y; - *z = data.z; - *w = data.w; -} - -void shader_set_data::env_vert_get(size_t index, vec4& data) { - if (!this || index >= SHADER_MAX_PROGRAM_ENV_PARAMETERS) - return; - - data = this->data.env.vert[index]; -} - -void shader_set_data::env_vert_get(size_t index, size_t count, vec4* data) { - if (!this || index >= SHADER_MAX_PROGRAM_ENV_PARAMETERS - || index + count > SHADER_MAX_PROGRAM_ENV_PARAMETERS || !data) - return; - - memcpy(data, &this->data.env.vert[index], sizeof(vec4) * count); -} - -void shader_set_data::state_clip_get_plane(size_t index, vec4& data) { - if (!this || index >= SHADER_MAX_CLIP_PLANES) - return; - - data = this->data.state.clip[index].plane; -} - -void shader_set_data::state_depth_get_range(vec4& data) { - if (!this) - return; - - data = this->data.state.depth.range; -} - -void shader_set_data::state_fog_get_color(vec4& data) { - if (!this) - return; - - data = this->data.state.fog.color; -} - -void shader_set_data::state_fog_get_params(vec4& data) { - if (!this) - return; - - data = this->data.state.fog.params; -} - -void shader_set_data::state_light_get_ambient(size_t index, vec4& data) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - data = this->data.state.light[index].ambient; -} - -void shader_set_data::state_light_get_diffuse(size_t index, vec4& data) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - data = this->data.state.light[index].ambient; -} - -void shader_set_data::state_light_get_specular(size_t index, vec4& data) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - data = this->data.state.light[index].ambient; -} - -void shader_set_data::state_light_get_position(size_t index, vec4& data) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - data = this->data.state.light[index].ambient; -} - -void shader_set_data::state_light_get_attenuation(size_t index, vec4& data) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - data = this->data.state.light[index].ambient; -} - -void shader_set_data::state_light_get_spot_direction(size_t index, vec4& data) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - data = this->data.state.light[index].ambient; -} - -void shader_set_data::state_light_get_half(size_t index, vec4& data) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - data = this->data.state.light[index].ambient; -} - -void shader_set_data::state_lightmodel_get_ambient(bool back, vec4& data) { - if (!this) - return; - - data = this->data.state.lightmodel[back ? 1 : 0].ambient; -} - -void shader_set_data::state_lightmodel_get_scene_color(bool back, vec4& data) { - if (!this) - return; - - data = this->data.state.lightmodel[back ? 1 : 0].scene_color; -} - -void shader_set_data::state_lightprod_get_ambient(bool back, size_t index, vec4& data) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - shader_state_lightprod* lightprod = back - ? this->data.state.lightprod_back : this->data.state.lightprod_front; - data = lightprod[index].ambient; -} - -void shader_set_data::state_lightprod_get_diffuse(bool back, size_t index, vec4& data) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - shader_state_lightprod* lightprod = back - ? this->data.state.lightprod_back : this->data.state.lightprod_front; - data = lightprod[index].diffuse; -} - -void shader_set_data::state_lightprod_get_specular(bool back, size_t index, vec4& data) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - shader_state_lightprod* lightprod = back - ? this->data.state.lightprod_back : this->data.state.lightprod_front; - data = lightprod[index].specular; -} - -void shader_set_data::state_material_get_ambient(bool back, vec4& data) { - if (!this) - return; - - data = this->data.state.material[back ? 1 : 0].ambient; -} - -void shader_set_data::state_material_get_diffuse(bool back, vec4& data) { - if (!this) - return; - - data = this->data.state.material[back ? 1 : 0].diffuse; -} - -void shader_set_data::state_material_get_specular(bool back, vec4& data) { - if (!this) - return; - - data = this->data.state.material[back ? 1 : 0].specular; -} - -void shader_set_data::state_material_get_emission(bool back, vec4& data) { - if (!this) - return; - - data = this->data.state.material[back ? 1 : 0].emission; -} - -void shader_set_data::state_material_get_shininess(bool back, vec4& data) { - if (!this) - return; - - data = this->data.state.material[back ? 1 : 0].shininess; -} - -void shader_set_data::state_matrix_get_modelview(size_t index, mat4& data) { - if (!this || index >= SHADER_MAX_VERTEX_UNITS) - return; - - data = this->data.state_matrix.modelview[index].mat; -} - -void shader_set_data::state_matrix_get_projection(mat4& data) { - if (!this) - return; - - data = this->data.state_matrix.projection.mat; -} - -void shader_set_data::state_matrix_get_mvp(mat4& data) { - if (!this) - return; - - data = this->data.state_matrix.mvp.mat; -} - -void shader_set_data::state_matrix_get_texture(size_t index, mat4& data) { - if (!this || index >= SHADER_MAX_TEXTURE_COORDS) - return; - - data = this->data.state_matrix.texture[index].mat; -} - -void shader_set_data::state_matrix_get_palette(size_t index, mat4& data) { - if (!this || index >= SHADER_MAX_PALETTE_MATRICES) - return; - - data = this->data.state_matrix.palette[index].mat; -} - -void shader_set_data::state_matrix_get_program(size_t index, mat4& data) { - if (!this || index >= SHADER_MAX_PROGRAM_MATRICES) - return; - - data = this->data.state_matrix.program[index].mat; -} - -void shader_set_data::state_point_get_size(vec4& data) { - if (!this) - return; - - data = this->data.state.point.size; -} - -void shader_set_data::state_point_get_attenuation(vec4& data) { - if (!this) - return; - - data = this->data.state.point.attenuation; -} - -void shader_set_data::state_texgen_get_eye_s(size_t index, vec4& data) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - data = this->data.state.texgen[index].eye.s; -} - -void shader_set_data::state_texgen_get_eye_t(size_t index, vec4& data) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - data = this->data.state.texgen[index].eye.t; -} - -void shader_set_data::state_texgen_get_eye_r(size_t index, vec4& data) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - data = this->data.state.texgen[index].eye.r; -} - -void shader_set_data::state_texgen_get_eye_q(size_t index, vec4& data) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - data = this->data.state.texgen[index].eye.q; -} - -void shader_set_data::state_texgen_get_object_s(size_t index, vec4& data) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - data = this->data.state.texgen[index].object.s; -} - -void shader_set_data::state_texgen_get_object_t(size_t index, vec4& data) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - data = this->data.state.texgen[index].object.t; -} - -void shader_set_data::state_texgen_get_object_r(size_t index, vec4& data) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - data = this->data.state.texgen[index].object.r; -} - -void shader_set_data::state_texgen_get_object_q(size_t index, vec4& data) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - data = this->data.state.texgen[index].object.q; -} - -void shader_set_data::state_texenv_get_color(size_t index, vec4& data) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - data = this->data.state.texenv[index].color; -} - -void shader_set_data::buffer_set(size_t index, const vec4& data) { - if (!this || index >= SHADER_MAX_PROGRAM_BUFFER_PARAMETERS) - return; - - if (!memcmp(&this->data.buffer.buffer[index], &data, sizeof(vec4))) - return; - - this->data.buffer.buffer[index] = data; - this->data.buffer_update_data = true; -} - -void shader_set_data::buffer_set(size_t index, size_t count, const vec4* data) { - if (!this || index >= SHADER_MAX_PROGRAM_BUFFER_PARAMETERS - || index + count > SHADER_MAX_PROGRAM_BUFFER_PARAMETERS || !data) - return; - - if (!memcmp(&this->data.buffer.buffer[index], data, sizeof(vec4) * count)) - return; - - memcpy(&this->data.buffer.buffer[index], data, sizeof(vec4) * count); - this->data.buffer_update_data = true; -} - -void shader_set_data::local_frag_set(size_t index, float_t x, float_t y, float_t z, float_t w) { - if (!this || !this->data.local || !this->data.local_update_data - || index >= SHADER_MAX_PROGRAM_LOCAL_PARAMETERS) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.local->frag[index], &data, sizeof(vec4))) - return; - - this->data.local->frag[index] = data; - this->data.local_update_data[1] = true; -} - -void shader_set_data::local_frag_set(size_t index, const vec4& data) { - if (!this || !this->data.local || !this->data.local_update_data - || index >= SHADER_MAX_PROGRAM_LOCAL_PARAMETERS) - return; - - if (!memcmp(&this->data.local->frag[index], &data, sizeof(vec4))) - return; - - this->data.local->frag[index] = data; - this->data.local_update_data[1] = true; -} - -void shader_set_data::local_frag_set(size_t index, size_t count, const vec4* data) { - if (!this || !this->data.local || !this->data.local_update_data - || index >= SHADER_MAX_PROGRAM_LOCAL_PARAMETERS - || index + count > SHADER_MAX_PROGRAM_LOCAL_PARAMETERS || !data) - return; - - if (!memcmp(&this->data.local->frag[index], data, sizeof(vec4) * count)) - return; - - memcpy(&this->data.local->frag[index], data, sizeof(vec4) * count); - this->data.local_update_data[1] = true; -} - -void shader_set_data::local_vert_set(size_t index, float_t x, float_t y, float_t z, float_t w) { - if (!this || !this->data.local || !this->data.local_update_data - || index >= SHADER_MAX_PROGRAM_LOCAL_PARAMETERS) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.local->vert[index], &data, sizeof(vec4))) - return; - - this->data.local->vert[index] = data; - this->data.local_update_data[0] = true; -} - -void shader_set_data::local_vert_set(size_t index, const vec4& data) { - if (!this || !this->data.local || !this->data.local_update_data - || index >= SHADER_MAX_PROGRAM_LOCAL_PARAMETERS) - return; - - if (!memcmp(&this->data.local->vert[index], &data, sizeof(vec4))) - return; - - this->data.local->vert[index] = data; - this->data.local_update_data[0] = true; -} - -void shader_set_data::local_vert_set(size_t index, size_t count, const vec4* data) { - if (!this || !this->data.local || !this->data.local_update_data - || index >= SHADER_MAX_PROGRAM_LOCAL_PARAMETERS - || index + count > SHADER_MAX_PROGRAM_LOCAL_PARAMETERS || !data) - return; - - if (!memcmp(&this->data.local->vert[index], data, sizeof(vec4) * count)) - return; - - memcpy(&this->data.local->vert[index], data, sizeof(vec4) * count); - this->data.local_update_data[0] = true; -} - -void shader_set_data::env_frag_set(size_t index, float_t x, float_t y, float_t z, float_t w) { - if (!this || index >= SHADER_MAX_PROGRAM_ENV_PARAMETERS) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.env.frag[index], &data, sizeof(vec4))) - return; - - this->data.env.frag[index] = data; - this->data.env_update_data = true; -} - -void shader_set_data::env_frag_set(size_t index, const vec4& data) { - if (!this || index >= SHADER_MAX_PROGRAM_ENV_PARAMETERS) - return; - - if (!memcmp(&this->data.env.frag[index], &data, sizeof(vec4))) - return; - - this->data.env.frag[index] = data; - this->data.env_update_data = true; -} - -void shader_set_data::env_frag_set(size_t index, size_t count, const vec4* data) { - if (!this || index >= SHADER_MAX_PROGRAM_ENV_PARAMETERS - || index + count > SHADER_MAX_PROGRAM_ENV_PARAMETERS) - return; - - if (!memcmp(&this->data.env.frag[index], data, sizeof(vec4) * count)) - return; - - memcpy(&this->data.env.frag[index], data, sizeof(vec4) * count); - this->data.env_update_data = true; -} - -void shader_set_data::env_vert_set(size_t index, float_t x, float_t y, float_t z, float_t w) { - if (!this || index >= SHADER_MAX_PROGRAM_ENV_PARAMETERS) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.env.vert[index], &data, sizeof(vec4))) - return; - - this->data.env.vert[index] = data; - this->data.env_update_data = true; -} - -void shader_set_data::env_vert_set(size_t index, const vec4& data) { - if (!this || index >= SHADER_MAX_PROGRAM_ENV_PARAMETERS) - return; - - if (!memcmp(&this->data.env.vert[index], &data, sizeof(vec4))) - return; - - this->data.env.vert[index] = data; - this->data.env_update_data = true; -} - -void shader_set_data::env_vert_set(size_t index, size_t count, const vec4* data) { - if (!this || index >= SHADER_MAX_PROGRAM_ENV_PARAMETERS - || index + count > SHADER_MAX_PROGRAM_ENV_PARAMETERS || !data) - return; - - if (!memcmp(&this->data.env.vert[index], data, sizeof(vec4) * count)) - return; - - memcpy(&this->data.env.vert[index], data, sizeof(vec4) * count); - this->data.env_update_data = true; -} - -void shader_set_data::state_clip_set_plane(size_t index, float_t x, float_t y, float_t z, float_t w) { - if (!this || index >= SHADER_MAX_CLIP_PLANES) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.clip[index].plane, &data, sizeof(vec4))) - return; - - this->data.state.clip[index].plane = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_clip_set_plane(size_t index, const vec4& data) { - if (!this || index >= SHADER_MAX_CLIP_PLANES) - return; - - if (!memcmp(&this->data.state.clip[index].plane, &data, sizeof(vec4))) - return; - - this->data.state.clip[index].plane = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_depth_set_range(float_t x, float_t y, float_t z, float_t w) { - if (!this) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.depth.range, &data, sizeof(vec4))) - return; - - this->data.state.depth.range = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_depth_set_range(const vec4& data) { - if (!this) - return; - - if (!memcmp(&this->data.state.depth.range, &data, sizeof(vec4))) - return; - - this->data.state.depth.range = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_fog_set_color(float_t x, float_t y, float_t z, float_t w) { - if (!this) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.fog.color, &data, sizeof(vec4))) - return; - - this->data.state.fog.color = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_fog_set_color(const vec4& data) { - if (!this) - return; - - if (!memcmp(&this->data.state.fog.color, &data, sizeof(vec4))) - return; - - this->data.state.fog.color = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_fog_set_params(float_t x, float_t y, float_t z, float_t w) { - if (!this) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.fog.params, &data, sizeof(vec4))) - return; - - this->data.state.fog.params = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_fog_set_params(const vec4& data) { - if (!this) - return; - - if (!memcmp(&this->data.state.fog.params, &data, sizeof(vec4))) - return; - - this->data.state.fog.params = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_light_set_ambient(size_t index, float_t x, float_t y, float_t z, float_t w) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.light[index].ambient, &data, sizeof(vec4))) - return; - - this->data.state.light[index].ambient = data; - this->data.state_update_data = true; - - vec4 lightprod_front = this->data.state.light[index].ambient * this->data.state.material[0].ambient; - vec4 lightprod_back = this->data.state.light[index].ambient * this->data.state.material[1].ambient; - state_lightprod_set_ambient(false, index, lightprod_front); - state_lightprod_set_ambient(true, index, lightprod_back); -} - -void shader_set_data::state_light_set_ambient(size_t index, const vec4& data) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - if (!memcmp(&this->data.state.light[index].ambient, &data, sizeof(vec4))) - return; - - this->data.state.light[index].ambient = data; - this->data.state_update_data = true; - - vec4 lightprod_front = this->data.state.light[index].ambient * this->data.state.material[0].ambient; - vec4 lightprod_back = this->data.state.light[index].ambient * this->data.state.material[1].ambient; - state_lightprod_set_ambient(false, index, lightprod_front); - state_lightprod_set_ambient(true, index, lightprod_back); -} - -void shader_set_data::state_light_set_diffuse(size_t index, float_t x, float_t y, float_t z, float_t w) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.light[index].diffuse, &data, sizeof(vec4))) - return; - - this->data.state.light[index].diffuse = data; - this->data.state_update_data = true; - - vec4 lightprod_front = this->data.state.light[index].diffuse * this->data.state.material[0].diffuse; - vec4 lightprod_back = this->data.state.light[index].diffuse * this->data.state.material[1].diffuse; - state_lightprod_set_diffuse(false, index, lightprod_front); - state_lightprod_set_diffuse(true, index, lightprod_back); -} - -void shader_set_data::state_light_set_diffuse(size_t index, const vec4& data) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - if (!memcmp(&this->data.state.light[index].diffuse, &data, sizeof(vec4))) - return; - - this->data.state.light[index].diffuse = data; - this->data.state_update_data = true; - - vec4 lightprod_front = this->data.state.light[index].diffuse * this->data.state.material[0].diffuse; - vec4 lightprod_back = this->data.state.light[index].diffuse * this->data.state.material[1].diffuse; - state_lightprod_set_diffuse(false, index, lightprod_front); - state_lightprod_set_diffuse(true, index, lightprod_back); -} - -void shader_set_data::state_light_set_specular(size_t index, float_t x, float_t y, float_t z, float_t w) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.light[index].specular, &data, sizeof(vec4))) - return; - - this->data.state.light[index].specular = data; - this->data.state_update_data = true; - - vec4 lightprod_front = this->data.state.light[index].specular * this->data.state.material[0].diffuse; - vec4 lightprod_back = this->data.state.light[index].specular * this->data.state.material[1].diffuse; - state_lightprod_set_specular(false, index, lightprod_front); - state_lightprod_set_specular(true, index, lightprod_back); -} - -void shader_set_data::state_light_set_specular(size_t index, const vec4& data) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - if (!memcmp(&this->data.state.light[index].specular, &data, sizeof(vec4))) - return; - - this->data.state.light[index].specular = data; - this->data.state_update_data = true; - - vec4 lightprod_front = this->data.state.light[index].specular * this->data.state.material[0].diffuse; - vec4 lightprod_back = this->data.state.light[index].specular * this->data.state.material[1].diffuse; - state_lightprod_set_specular(false, index, lightprod_front); - state_lightprod_set_specular(true, index, lightprod_back); -} - -void shader_set_data::state_light_set_position(size_t index, float_t x, float_t y, float_t z, float_t w) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.light[index].position, &data, sizeof(vec4))) - return; - - this->data.state.light[index].position = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_light_set_position(size_t index, const vec4& data) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - if (!memcmp(&this->data.state.light[index].position, &data, sizeof(vec4))) - return; - - this->data.state.light[index].position = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_light_set_attenuation(size_t index, float_t x, float_t y, float_t z, float_t w) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.light[index].attenuation, &data, sizeof(vec4))) - return; - - this->data.state.light[index].attenuation = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_light_set_attenuation(size_t index, const vec4& data) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - if (!memcmp(&this->data.state.light[index].attenuation, &data, sizeof(vec4))) - return; - - this->data.state.light[index].attenuation = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_light_set_spot_direction(size_t index, float_t x, float_t y, float_t z, float_t w) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.light[index].spot_direction, &data, sizeof(vec4))) - return; - - this->data.state.light[index].spot_direction = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_light_set_spot_direction(size_t index, const vec4& data) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - if (!memcmp(&this->data.state.light[index].spot_direction, &data, sizeof(vec4))) - return; - - this->data.state.light[index].spot_direction = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_light_set_half(size_t index, float_t x, float_t y, float_t z, float_t w) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.light[index].half, &data, sizeof(vec4))) - return; - - this->data.state.light[index].half = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_light_set_half(size_t index, const vec4& data) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - if (!memcmp(&this->data.state.light[index].half, &data, sizeof(vec4))) - return; - - this->data.state.light[index].half = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_lightmodel_set_ambient(bool back, float_t x, float_t y, float_t z, float_t w) { - if (!this) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.lightmodel[back ? 1 : 0].ambient, &data, sizeof(vec4))) - return; - - this->data.state.lightmodel[back ? 1 : 0].ambient = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_lightmodel_set_ambient(bool back, const vec4& data) { - if (!this) - return; - - if (!memcmp(&this->data.state.lightmodel[back ? 1 : 0].ambient, &data, sizeof(vec4))) - return; - - this->data.state.lightmodel[back ? 1 : 0].ambient = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_lightmodel_set_scene_color(bool back, float_t x, float_t y, float_t z, float_t w) { - if (!this) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.lightmodel[back ? 1 : 0].scene_color, &data, sizeof(vec4))) - return; - - this->data.state.lightmodel[back ? 1 : 0].scene_color = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_lightmodel_set_scene_color(bool back, const vec4& data) { - if (!this) - return; - - if (!memcmp(&this->data.state.lightmodel[back ? 1 : 0].scene_color, &data, sizeof(vec4))) - return; - - this->data.state.lightmodel[back ? 1 : 0].scene_color = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_lightprod_set_ambient(bool back, - size_t index, float_t x, float_t y, float_t z, float_t w) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - shader_state_lightprod* lightprod = back - ? data.state.lightprod_back : data.state.lightprod_front; - - vec4 data = { x, y, z, w }; - if (!memcmp(&lightprod[index].ambient, &data, sizeof(vec4))) - return; - - lightprod[index].ambient = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_lightprod_set_ambient(bool back, size_t index, const vec4& data) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - shader_state_lightprod* lightprod = back - ? this->data.state.lightprod_back : this->data.state.lightprod_front; - - if (!memcmp(&lightprod[index].ambient, &data, sizeof(vec4))) - return; - - lightprod[index].ambient = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_lightprod_set_diffuse(bool back, - size_t index, float_t x, float_t y, float_t z, float_t w) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - shader_state_lightprod* lightprod = back - ? data.state.lightprod_back : data.state.lightprod_front; - - vec4 data = { x, y, z, w }; - if (!memcmp(&lightprod[index].diffuse, &data, sizeof(vec4))) - return; - - lightprod[index].diffuse = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_lightprod_set_diffuse(bool back, size_t index, const vec4& data) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - shader_state_lightprod* lightprod = back - ? this->data.state.lightprod_back : this->data.state.lightprod_front; - - if (!memcmp(&lightprod[index].diffuse, &data, sizeof(vec4))) - return; - - lightprod[index].diffuse = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_lightprod_set_specular(bool back, - size_t index, float_t x, float_t y, float_t z, float_t w) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - shader_state_lightprod* lightprod = back - ? data.state.lightprod_back : data.state.lightprod_front; - - vec4 data = { x, y, z, w }; - if (!memcmp(&lightprod[index].specular, &data, sizeof(vec4))) - return; - - lightprod[index].specular = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_lightprod_set_specular(bool back, - size_t index, const vec4& data) { - if (!this || index >= SHADER_MAX_LIGHTS) - return; - - shader_state_lightprod* lightprod = back - ? this->data.state.lightprod_back : this->data.state.lightprod_front; - - if (!memcmp(&lightprod[index].specular, &data, sizeof(vec4))) - return; - - lightprod[index].specular = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_material_set_ambient(bool back, float_t x, float_t y, float_t z, float_t w) { - if (!this) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.material[back ? 1 : 0].ambient, &data, sizeof(vec4))) - return; - - this->data.state.material[back ? 1 : 0].ambient = data; - this->data.state_update_data = true; - - for (int32_t index = 0; index < SHADER_MAX_LIGHTS; index++) { - vec4 lightprod = this->data.state.light[index].ambient - * this->data.state.material[back ? 1 : 0].ambient; - state_lightprod_set_ambient(back, index, lightprod); + this->shaders = 0; +} + +shader_vulkan_pair shader_vulkan::get(shader_set_data* set, uint32_t sub_index) { + shader_vulkan_data* vulkan_data = set->vulkan_data; + vulkan_data->curr_vp = 0; + vulkan_data->curr_fp = 0; + + if (num_sub < 1) + return {}; + + int32_t sub_shader_index = 0; + for (shader_vulkan_sub* i = sub; i->sub_index != sub_index; i++) + if (++sub_shader_index >= num_sub) + return {}; + + shader_vulkan_sub* sub_shader = &sub[sub_shader_index]; + + int32_t unival_vp_curr = 1; + int32_t unival_fp_curr = 1; + int32_t unival_vp = 0; + int32_t unival_fp = 0; + int32_t uniform_val[SHADER_MAX_UNIFORM_VALUES] = {}; + if (num_uniform > 0) { + const int32_t* vp_unival_max = sub_shader->vp_unival_max; + const int32_t* fp_unival_max = sub_shader->fp_unival_max; + + int32_t i = 0; + for (i = 0; i < num_uniform && i < sizeof(uniform_val) / sizeof(int32_t); i++) { + const int32_t unival = uniform_value[use_uniform[i]]; + const int32_t unival_vp_max = use_permut[i] ? vp_unival_max[i] : 0; + const int32_t unival_fp_max = use_permut[i] ? fp_unival_max[i] : 0; + unival_vp += unival_vp_curr * min_def(unival, unival_vp_max); + unival_fp += unival_fp_curr * min_def(unival, unival_fp_max); + unival_vp_curr *= unival_vp_max + 1; + unival_fp_curr *= unival_fp_max + 1; + + int32_t unival_max_spv = max_def(vp_unival_max[i], fp_unival_max[i]); + uniform_val[i] = min_def(unival, unival_max_spv); + } + } + + shader_vulkan_sub_shader* shader_vert = &sub_shader->vp[unival_vp]; + shader_vulkan_sub_shader* shader_frag = &sub_shader->fp[unival_fp]; + vulkan_data->curr_vp = shader_vert; + vulkan_data->curr_fp = shader_frag; + return { shader_vert->shader_module, shader_frag->shader_module }; +} + +shader_vulkan_data::shader_vulkan_data(VkDevice device, VmaAllocator allocator, farc* f, + const shader_table* shaders_table, const size_t size, + const shader_vulkan_get_func* get_func_table, const size_t get_func_table_size, + PFNSHADERGETINDEXFUNCPROC get_index_by_name) : size(), shaders(), curr_vp(), curr_fp(), + primitive_restart(), uniform_val(), device(), allocator(), get_index_by_name() { + wchar_t temp_buf[MAX_PATH]; + if (FAILED(SHGetFolderPathW(0, CSIDL_LOCAL_APPDATA, 0, 0, temp_buf))) + return; + + char vert_buf[MAX_PATH]; + char frag_buf[MAX_PATH]; + + shader_vulkan* shaders = force_malloc_s(shader_vulkan, size); + this->shaders = shaders; + this->size = size; + for (size_t i = 0; i < size; i++) { + shader_vulkan* shader = &shaders[i]; + shader->name = shaders_table[i].name; + shader->name_index = shaders_table[i].name_index; + shader->num_sub = shaders_table[i].num_sub; + shader->sub = force_malloc_s(shader_vulkan_sub, shader->num_sub); + shader->num_uniform = shaders_table[i].num_uniform; + shader->use_uniform = shaders_table[i].use_uniform; + shader->use_permut = shaders_table[i].use_permut; + + int32_t num_sub = shader->num_sub; + const shader_sub_table* sub_table = shaders_table[i].sub; + shader_vulkan_sub* sub = shader->sub; + for (size_t j = 0; j < num_sub; j++, sub++, sub_table++) { + sub->sub_index = sub_table->sub_index; + sub->vp_unival_max = sub_table->vp_unival_max; + sub->fp_unival_max = sub_table->fp_unival_max; + + int32_t unival_vp_count = 1; + int32_t unival_fp_count = 1; + if (shader->num_uniform > 0 + && (sub_table->vp_unival_max[0] != -1 || sub_table->fp_unival_max[0] != -1)) { + int32_t num_uniform = shader->num_uniform; + int32_t unival_vp_curr = 1; + int32_t unival_fp_curr = 1; + const int32_t* vp_unival_max = sub_table->vp_unival_max; + const int32_t* fp_unival_max = sub_table->fp_unival_max; + for (size_t k = 0; k < num_uniform; k++) { + const int32_t unival_vp_max = shader->use_permut[k] ? vp_unival_max[k] : 0; + const int32_t unival_fp_max = shader->use_permut[k] ? fp_unival_max[k] : 0; + unival_vp_count += unival_vp_curr * unival_vp_max; + unival_fp_count += unival_fp_curr * unival_fp_max; + unival_vp_curr *= unival_vp_max + 1; + unival_fp_curr *= unival_fp_max + 1; + } + } + + shader_vulkan_sub_shader* vp = force_malloc_s(shader_vulkan_sub_shader, unival_vp_count); + shader_vulkan_sub_shader* fp = force_malloc_s(shader_vulkan_sub_shader, unival_fp_count); + sub->vp = vp; + sub->fp = fp; + + VkBufferCreateInfo buffer_create_info = {}; + buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + buffer_create_info.size = sizeof(vec4) * SHADER_MAX_PROGRAM_LOCAL_PARAMETERS; + buffer_create_info.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + buffer_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + + VmaAllocationCreateInfo allocation_create_info = {}; + allocation_create_info.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; + allocation_create_info.flags = 0; + + strcpy_s(vert_buf, sizeof(vert_buf), sub_table->vp); + strcat_s(vert_buf, sizeof(vert_buf), ".vert.bin"); + + farc_file* shader_vert_file = f->read_file(vert_buf); + program_spv* vert_spv = (program_spv*)shader_vert_file->data; + size_t vert_spv_data_base = (size_t)shader_vert_file->data; + for (size_t k = 0; k < unival_vp_count; k++, vert_spv++) { + VkShaderModuleCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + create_info.codeSize = vert_spv->size; + create_info.pCode = (uint32_t*)(vert_spv_data_base + vert_spv->spv); + if (!vert_spv->size || vkCreateShaderModule(device, + &create_info, 0, &vp[k].shader_module) != VK_SUCCESS) { + vp[k].shader_module = 0; + } + } + + strcpy_s(frag_buf, sizeof(frag_buf), sub_table->fp); + strcat_s(frag_buf, sizeof(frag_buf), ".frag.bin"); + + farc_file* shader_frag_file = f->read_file(frag_buf); + program_spv* frag_spv = (program_spv*)shader_frag_file->data; + size_t frag_spv_data_base = (size_t)shader_frag_file->data; + for (size_t k = 0; k < unival_fp_count; k++, frag_spv++) { + VkShaderModuleCreateInfo create_info = {}; + create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; + create_info.codeSize = frag_spv->size; + create_info.pCode = (uint32_t*)(frag_spv_data_base + frag_spv->spv); + if (!vert_spv->size || vkCreateShaderModule(device, + &create_info, 0, &fp[k].shader_module) != VK_SUCCESS) { + fp[k].shader_module = 0; + } + } + } + + for (size_t j = 0; j < get_func_table_size; j++) + if (shader->name_index == get_func_table[j].name_index) { + shader->get_func = get_func_table[j].get_func; + break; + } + } + + this->device = device; + this->allocator = allocator; + + this->get_index_by_name = get_index_by_name; +} + +shader_vulkan_data::~shader_vulkan_data() { + size_t size = this->size; + shader_vulkan* shaders = this->shaders; + for (size_t i = 0; i < size; i++) { + shader_vulkan* shader = &shaders[i]; + int32_t num_sub = shader->num_sub; + shader_vulkan_sub* sub = shader->sub; + for (size_t j = 0; j < num_sub; j++, sub++) { + int32_t unival_vp_count = 1; + int32_t unival_fp_count = 1; + if (shader->num_uniform > 0) { + int32_t num_uniform = shader->num_uniform; + int32_t unival_vp_curr = 1; + int32_t unival_fp_curr = 1; + const int32_t* vp_unival_max = sub->vp_unival_max; + const int32_t* fp_unival_max = sub->fp_unival_max; + for (size_t k = 0; k < num_uniform; k++) { + const int32_t unival_vp_max = shader->use_permut[k] ? vp_unival_max[k] : 0; + const int32_t unival_fp_max = shader->use_permut[k] ? fp_unival_max[k] : 0; + unival_vp_count += unival_vp_curr * unival_vp_max; + unival_fp_count += unival_fp_curr * unival_fp_max; + unival_vp_curr *= unival_vp_max + 1; + unival_fp_curr *= unival_fp_max + 1; + } + } + + if (sub->vp) { + shader_vulkan_sub_shader* vp = sub->vp; + for (size_t k = 0; k < unival_vp_count; k++) + if (vp[k].shader_module) + vkDestroyShaderModule(device, vp[k].shader_module, 0); + free(vp); + sub->vp = 0; + } + + if (sub->fp) { + shader_vulkan_sub_shader* fp = sub->fp; + for (size_t k = 0; k < unival_fp_count; k++) + if (fp[k].shader_module) + vkDestroyShaderModule(device, fp[k].shader_module, 0); + free(fp); + sub->fp = 0; + } + } + free_def(shader->sub); + } + free_def(shaders); + this->shaders = 0; + + device = 0; + allocator = 0; +} + +shader_set_data::shader_set_data() : opengl_data(), vulkan_data() { + +} + +void shader_set_data::disable_primitive_restart() { + if (vulkan_render) + vulkan_data->primitive_restart = false; + else + opengl_data->primitive_restart = false; +} + +void shader_set_data::draw_arrays(GLenum mode, GLint first, GLsizei count) { + shader_update_data(this); + glDrawArrays(mode, first, count); +} + +void shader_set_data::draw_elements(GLenum mode, + GLsizei count, GLenum type, const void* indices) { + switch (mode) { + case GL_TRIANGLE_STRIP: + uint32_t index; + switch (type) { + case GL_UNSIGNED_BYTE: + index = 0xFF; + break; + case GL_UNSIGNED_SHORT: + index = 0xFFFF; + break; + case GL_UNSIGNED_INT: + default: + index = 0xFFFFFFFF; + break; + } + + enable_primitive_restart(); + + if (!vulkan_render && opengl_data->primitive_restart_index != index) + opengl_data->primitive_restart_index = index; + break; + } + + shader_update_data(this); + glDrawElements(mode, count, type, indices); + + switch (mode) { + case GL_TRIANGLE_STRIP: + disable_primitive_restart(); + break; } } -void shader_set_data::state_material_set_ambient(bool back, const vec4& data) { - if (!this) - return; +void shader_set_data::draw_range_elements(GLenum mode, + GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices) { + switch (mode) { + case GL_TRIANGLE_STRIP: + uint32_t index; + switch (type) { + case GL_UNSIGNED_BYTE: + index = 0xFF; + break; + case GL_UNSIGNED_SHORT: + index = 0xFFFF; + break; + case GL_UNSIGNED_INT: + default: + index = 0xFFFFFFFF; + break; + } - if (!memcmp(&this->data.state.material[back ? 1 : 0].ambient, &data, sizeof(vec4))) - return; + enable_primitive_restart(); - this->data.state.material[back ? 1 : 0].ambient = data; - this->data.state_update_data = true; + if (!vulkan_render && opengl_data->primitive_restart_index != index) + opengl_data->primitive_restart_index = index; + break; + } - for (int32_t index = 0; index < SHADER_MAX_LIGHTS; index++) { - vec4 lightprod = this->data.state.light[index].ambient - * this->data.state.material[back ? 1 : 0].ambient; - state_lightprod_set_ambient(back, index, lightprod); + shader_update_data(this); + glDrawRangeElements(mode, start, end, count, type, indices); + + switch (mode) { + case GL_TRIANGLE_STRIP: + disable_primitive_restart(); + break; } } -void shader_set_data::state_material_set_diffuse(bool back, float_t x, float_t y, float_t z, float_t w) { - if (!this) +void shader_set_data::enable_primitive_restart() { + if (vulkan_render) + vulkan_data->primitive_restart = true; + else + opengl_data->primitive_restart = true; +} + +int32_t shader_set_data::get_index_by_name(const char* name) { + if (vulkan_render) { + size_t size = vulkan_data->size; + shader_vulkan* shaders = vulkan_data->shaders; + + if (vulkan_data->get_index_by_name) { + int32_t index = vulkan_data->get_index_by_name(name); + if (index != -1) + return index; + } + + for (size_t i = 0; i < size; i++) + if (!str_utils_compare(shaders[i].name, name)) + return (int32_t)shaders[i].name_index; + } + else { + size_t size = opengl_data->size; + shader_opengl* shaders = opengl_data->shaders; + + if (opengl_data->get_index_by_name) { + int32_t index = opengl_data->get_index_by_name(name); + if (index != -1) + return index; + } + + for (size_t i = 0; i < size; i++) + if (!str_utils_compare(shaders[i].name, name)) + return (int32_t)shaders[i].name_index; + } + return -1; +} + +void shader_set_data::load_opengl(farc* f, bool ignore_cache, + const char* name, const shader_table* shaders_table, const size_t size, + const shader_opengl_bind_func* bind_func_table, const size_t bind_func_table_size, + PFNSHADERGETINDEXFUNCPROC get_index_by_name) { + if (!this || !f || !shaders_table || !size) return; - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.material[back ? 1 : 0].diffuse, &data, sizeof(vec4))) + opengl_data = new shader_opengl_data(f, ignore_cache, name, + shaders_table, size, bind_func_table, bind_func_table_size, get_index_by_name); + vulkan_data = 0; +} + +void shader_set_data::load_vulkan(VkDevice device, VmaAllocator allocator, farc* f, + const shader_table* shaders_table, const size_t size, + const shader_vulkan_get_func* get_func_table, const size_t get_func_table_size, + PFNSHADERGETINDEXFUNCPROC get_index_by_name) { + if (!this || !device || !allocator || !f || !shaders_table || !size) return; - this->data.state.material[back ? 1 : 0].diffuse = data; - this->data.state_update_data = true; + opengl_data = 0; + vulkan_data = new shader_vulkan_data(device, allocator, f, + shaders_table, size, get_func_table, get_func_table_size, get_index_by_name); +} - for (int32_t index = 0; index < SHADER_MAX_LIGHTS; index++) { - vec4 lightprod = this->data.state.light[index].diffuse - * this->data.state.material[back ? 1 : 0].diffuse; - state_lightprod_set_diffuse(back, index, lightprod); +void shader_set_data::set_opengl_shader(uint32_t index) { + if (vulkan_render || !opengl_data) + return; + + if (this && index && index != -1) { + shader_opengl* shader = &opengl_data->shaders[index]; + if (shader->bind_func) + shader->bind_func(this, shader); + else + shader->bind(this, shader->sub[0].sub_index); + } + else + shader_opengl::unbind(); +} + +std::pair shader_set_data::get_vulkan_shader(uint32_t index) { + if (!vulkan_render || !vulkan_data) + return {}; + + if (this && index && index != -1) { + shader_vulkan* shader = &vulkan_data->shaders[index]; + if (shader->get_func) + return shader->get_func(this, shader); + else + return shader->get(this, shader->sub[0].sub_index); + } + else { + vulkan_data->curr_vp = 0; + vulkan_data->curr_fp = 0; + return {}; } } -void shader_set_data::state_material_set_diffuse(bool back, const vec4& data) { - if (!this) - return; - - if (!memcmp(&this->data.state. material[back ? 1 : 0].diffuse, &data, sizeof(vec4))) - return; - - this->data.state. material[back ? 1 : 0].diffuse = data; - this->data.state_update_data = true; - - for (int32_t index = 0; index < SHADER_MAX_LIGHTS; index++) { - vec4 lightprod = this->data.state.light[index].diffuse - * this->data.state.material[back ? 1 : 0].diffuse; - state_lightprod_set_diffuse(back, index, lightprod); +void shader_set_data::unload_opengl() { + if (opengl_data) { + delete opengl_data; + opengl_data = 0; } } -void shader_set_data::state_material_set_specular(bool back, float_t x, float_t y, float_t z, float_t w) { - if (!this) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.material[back ? 1 : 0].specular, &data, sizeof(vec4))) - return; - - this->data.state.material[back ? 1 : 0].specular = data; - this->data.state_update_data = true; - - for (int32_t index = 0; index < SHADER_MAX_LIGHTS; index++) { - vec4 lightprod = this->data.state.light[index].specular - * this->data.state.material[back ? 1 : 0].specular; - state_lightprod_set_specular(back, index, lightprod); +void shader_set_data::unload_vulkan() { + if (vulkan_data) { + delete vulkan_data; + vulkan_data = 0; } } -void shader_set_data::state_material_set_specular(bool back, const vec4& data) { - if (!this) - return; - - if (!memcmp(&this->data.state.material[back ? 1 : 0].specular, &data, sizeof(vec4))) - return; - - this->data.state.material[back ? 1 : 0].specular = data; - this->data.state_update_data = true; - - for (int32_t index = 0; index < SHADER_MAX_LIGHTS; index++) { - vec4 lightprod = this->data.state.light[index].specular - * this->data.state.material[back ? 1 : 0].specular; - state_lightprod_set_specular(back, index, lightprod); - } -} - -void shader_set_data::state_material_set_emission(bool back, float_t x, float_t y, float_t z, float_t w) { - if (!this) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.material[back ? 1 : 0].emission, &data, sizeof(vec4))) - return; - - this->data.state.material[back ? 1 : 0].emission = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_material_set_emission(bool back, const vec4& data) { - if (!this) - return; - - if (!memcmp(&this->data.state.material[back ? 1 : 0].emission, &data, sizeof(vec4))) - return; - - this->data.state.material[back ? 1 : 0].emission = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_material_set_shininess(bool back, float_t x, float_t y, float_t z, float_t w) { - if (!this) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.material[back ? 1 : 0].shininess, &data, sizeof(vec4))) - return; - - this->data.state.material[back ? 1 : 0].shininess = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_material_set_shininess(bool back, const vec4& data) { - if (!this) - return; - - if (!memcmp(&this->data.state.material[back ? 1 : 0].shininess, &data, sizeof(vec4))) - return; - - this->data.state.material[back ? 1 : 0].shininess = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_matrix_set_modelview(const mat4& data, bool mult) { - state_matrix_set_modelview(0, data, mult); -} - -void shader_set_data::state_matrix_set_modelview(size_t index, const mat4& data, bool mult) { - if (!this || index >= SHADER_MAX_VERTEX_UNITS) - return; - - if (memcmp(&this->data.state_matrix.modelview[index].mat, &data, sizeof(mat4))) { - mat4 mat; - mat4 mat_inv; - mat4 mat_trans; - mat4 mat_invtrans; - - mat = data; - mat4_inverse(&mat, &mat_inv); - mat4_transpose(&mat, &mat_trans); - mat4_transpose(&mat_inv, &mat_invtrans); - this->data.state_matrix.modelview[index].mat = mat; - this->data.state_matrix.modelview[index].inv = mat_inv; - this->data.state_matrix.modelview[index].trans = mat_trans; - this->data.state_matrix.modelview[index].invtrans = mat_invtrans; - this->data.state_matrix_update_data = true; - } - - if (mult && index == 0) { - mat4 mat; - mat4_mult(&this->data.state_matrix.modelview[0].mat, - &this->data.state_matrix.projection.mat, &mat); - state_matrix_set_mvp(mat); - } -} - -void shader_set_data::state_matrix_set_projection(const mat4& data, bool mult) { - if (!this) - return; - - if (memcmp(&this->data.state_matrix.projection.mat, &data, sizeof(mat4))) { - mat4 mat; - mat4 mat_inv; - mat4 mat_trans; - mat4 mat_invtrans; - - mat = data; - mat4_inverse(&mat, &mat_inv); - mat4_transpose(&mat, &mat_trans); - mat4_transpose(&mat_inv, &mat_invtrans); - this->data.state_matrix.projection.mat = mat; - this->data.state_matrix.projection.inv = mat_inv; - this->data.state_matrix.projection.trans = mat_trans; - this->data.state_matrix.projection.invtrans = mat_invtrans; - this->data.state_matrix_update_data = true; - } - - if (mult) { - mat4 mat; - mat4_mult(&this->data.state_matrix.modelview[0].mat, - &this->data.state_matrix.projection.mat, &mat); - state_matrix_set_mvp(mat); - } -} - -void shader_set_data::state_matrix_set_mvp(const mat4& data) { - if (!this) - return; - - if (!memcmp(&this->data.state_matrix.mvp.mat, &data, sizeof(mat4))) - return; - - mat4 mat; - mat4 mat_inv; - mat4 mat_trans; - mat4 mat_invtrans; - - mat = data; - mat4_inverse(&mat, &mat_inv); - mat4_transpose(&mat, &mat_trans); - mat4_transpose(&mat_inv, &mat_invtrans); - this->data.state_matrix.mvp.mat = mat; - this->data.state_matrix.mvp.inv = mat_inv; - this->data.state_matrix.mvp.trans = mat_trans; - this->data.state_matrix.mvp.invtrans = mat_invtrans; - this->data.state_matrix_update_data = true; -} - -void shader_set_data::state_matrix_set_modelview(const mat4& model, const mat4& view, bool mult) { - state_matrix_set_modelview(0, model, view, mult); -} - -void shader_set_data::state_matrix_set_modelview(size_t index, const mat4& model, const mat4& view, bool mult) { - mat4 mv; - mat4_mult(&model, &view, &mv); - state_matrix_set_modelview(index, mv, true); -} - -void shader_set_data::state_matrix_set_mvp(const mat4& modelview, const mat4& projection) { - state_matrix_set_modelview(modelview, false); - state_matrix_set_projection(projection, true); -} - -void shader_set_data::state_matrix_set_mvp(const mat4& model, const mat4& view, const mat4& projection) { - mat4 mv; - mat4_mult(&model, &view, &mv); - state_matrix_set_modelview(0, mv, false); - state_matrix_set_projection(projection, true); -} - -void shader_set_data::state_matrix_set_texture(size_t index, const mat4& data) { - if (!this || index >= SHADER_MAX_TEXTURE_COORDS) - return; - - if (!memcmp(&this->data.state_matrix.texture[index].mat, &data, sizeof(mat4))) - return; - - mat4 mat; - mat4 mat_inv; - mat4 mat_trans; - mat4 mat_invtrans; - - mat = data; - mat4_inverse(&mat, &mat_inv); - mat4_transpose(&mat, &mat_trans); - mat4_transpose(&mat_inv, &mat_invtrans); - this->data.state_matrix.texture[index].mat = mat; - this->data.state_matrix.texture[index].inv = mat_inv; - this->data.state_matrix.texture[index].trans = mat_trans; - this->data.state_matrix.texture[index].invtrans = mat_invtrans; - this->data.state_matrix_update_data = true; -} - -void shader_set_data::state_matrix_set_palette(size_t index, const mat4& data) { - if (!this || index >= SHADER_MAX_PALETTE_MATRICES) - return; - - if (!memcmp(&this->data.state_matrix.palette[index].mat, &data, sizeof(mat4))) - return; - - mat4 mat; - mat4 mat_inv; - mat4 mat_trans; - mat4 mat_invtrans; - - mat = data; - mat4_inverse(&mat, &mat_inv); - mat4_transpose(&mat, &mat_trans); - mat4_transpose(&mat_inv, &mat_invtrans); - this->data.state_matrix.palette[index].mat = mat; - this->data.state_matrix.palette[index].inv = mat_inv; - this->data.state_matrix.palette[index].trans = mat_trans; - this->data.state_matrix.palette[index].invtrans = mat_invtrans; - this->data.state_matrix_update_data = true; -} - -void shader_set_data::state_matrix_set_program(size_t index, const mat4& data) { - if (!this || index >= SHADER_MAX_PROGRAM_MATRICES) - return; - - if (!memcmp(&this->data.state_matrix.program[index].mat, &data, sizeof(mat4))) - return; - - mat4 mat; - mat4 mat_inv; - mat4 mat_trans; - mat4 mat_invtrans; - - mat = data; - mat4_inverse(&mat, &mat_inv); - mat4_transpose(&mat, &mat_trans); - mat4_transpose(&mat_inv, &mat_invtrans); - this->data.state_matrix.program[index].mat = mat; - this->data.state_matrix.program[index].inv = mat_inv; - this->data.state_matrix.program[index].trans = mat_trans; - this->data.state_matrix.program[index].invtrans = mat_invtrans; - this->data.state_matrix_update_data = true; -} - -void shader_set_data::state_point_set_size(float_t x, float_t y, float_t z, float_t w) { - if (!this) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.point.size, &data, sizeof(vec4))) - return; - - this->data.state.point.size = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_point_set_size(const vec4& data) { - if (!this) - return; - - if (!memcmp(&this->data.state.point.size, &data, sizeof(vec4))) - return; - - this->data.state.point.size = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_point_set_attenuation(float_t x, float_t y, float_t z, float_t w) { - if (!this) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.point.attenuation, &data, sizeof(vec4))) - return; - - this->data.state.point.attenuation = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_point_set_attenuation(const vec4& data) { - if (!this) - return; - - if (!memcmp(&this->data.state.point.attenuation, &data, sizeof(vec4))) - return; - - this->data.state.point.attenuation = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_texgen_set_eye_s(size_t index, float_t x, float_t y, float_t z, float_t w) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.texgen[index].eye.s, &data, sizeof(vec4))) - return; - - this->data.state.texgen[index].eye.s = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_texgen_set_eye_s(size_t index, const vec4& data) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - if (!memcmp(&this->data.state.texgen[index].eye.s, &data, sizeof(vec4))) - return; - - this->data.state.texgen[index].eye.s = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_texgen_set_eye_t(size_t index, float_t x, float_t y, float_t z, float_t w) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.texgen[index].eye.t, &data, sizeof(vec4))) - return; - - this->data.state.texgen[index].eye.t = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_texgen_set_eye_t(size_t index, const vec4& data) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - if (!memcmp(&this->data.state.texgen[index].eye.t, &data, sizeof(vec4))) - return; - - this->data.state.texgen[index].eye.t = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_texgen_set_eye_r(size_t index, float_t x, float_t y, float_t z, float_t w) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.texgen[index].eye.r, &data, sizeof(vec4))) - return; - - this->data.state.texgen[index].eye.r = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_texgen_set_eye_r(size_t index, const vec4& data) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - if (!memcmp(&this->data.state.texgen[index].eye.r, &data, sizeof(vec4))) - return; - - this->data.state.texgen[index].eye.r = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_texgen_set_eye_q(size_t index, float_t x, float_t y, float_t z, float_t w) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.texgen[index].eye.q, &data, sizeof(vec4))) - return; - - this->data.state.texgen[index].eye.q = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_texgen_set_eye_q(size_t index, const vec4& data) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - if (!memcmp(&this->data.state.texgen[index].eye.q, &data, sizeof(vec4))) - return; - - this->data.state.texgen[index].eye.q = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_texgen_set_object_s(size_t index, float_t x, float_t y, float_t z, float_t w) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.texgen[index].object.s, &data, sizeof(vec4))) - return; - - this->data.state.texgen[index].object.s = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_texgen_set_object_s(size_t index, const vec4& data) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - if (!memcmp(&this->data.state.texgen[index].object.s, &data, sizeof(vec4))) - return; - - this->data.state.texgen[index].object.s = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_texgen_set_object_t(size_t index, float_t x, float_t y, float_t z, float_t w) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.texgen[index].object.t, &data, sizeof(vec4))) - return; - - this->data.state.texgen[index].object.t = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_texgen_set_object_t(size_t index, const vec4& data) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - if (!memcmp(&this->data.state.texgen[index].object.t, &data, sizeof(vec4))) - return; - - this->data.state.texgen[index].object.t = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_texgen_set_object_r(size_t index, float_t x, float_t y, float_t z, float_t w) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.texgen[index].object.r, &data, sizeof(vec4))) - return; - - this->data.state.texgen[index].object.r = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_texgen_set_object_r(size_t index, const vec4& data) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - if (!memcmp(&this->data.state.texgen[index].object.r, &data, sizeof(vec4))) - return; - - this->data.state.texgen[index].object.r = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_texgen_set_object_q(size_t index, float_t x, float_t y, float_t z, float_t w) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.texgen[index].object.q, &data, sizeof(vec4))) - return; - - this->data.state.texgen[index].object.q = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_texgen_set_object_q(size_t index, const vec4& data) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - if (!memcmp(&this->data.state.texgen[index].object.q, &data, sizeof(vec4))) - return; - - this->data.state.texgen[index].object.q = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_texenv_set_color(size_t index, float_t x, float_t y, float_t z, float_t w) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - vec4 data = { x, y, z, w }; - if (!memcmp(&this->data.state.texenv[index].color, &data, sizeof(vec4))) - return; - - this->data.state.texenv[index].color = data; - this->data.state_update_data = true; -} - -void shader_set_data::state_texenv_set_color(size_t index, const vec4& data) { - if (!this || index >= SHADER_MAX_TEXTURE_UNITS) - return; - - if (!memcmp(&this->data.state.texenv[index].color, &data, sizeof(vec4))) - return; - - this->data.state.texenv[index].color = data; - this->data.state_update_data = true; -} - static GLuint shader_compile_shader(GLenum type, const char* data, const char* file) { if (!data) return 0; @@ -2367,7 +1057,16 @@ static GLuint shader_compile_shader(GLenum type, const char* data, const char* f if (!success) { GLchar* info_log = force_malloc_s(GLchar, 0x10000); glGetShaderInfoLog(shader, 0x10000, 0, info_log); - printf_debug("Shader compile error:\nfile: %s\n%s\n", file, info_log); + const char* type_str = "Unknown"; + switch (type) { + case GL_FRAGMENT_SHADER: + type_str = "Fragment"; + break; + case GL_VERTEX_SHADER: + type_str = "Vertex"; + break; + } + printf_debug("%s shader compile error:\nfile: %s\n%s\n", type_str, file, info_log); free_def(info_log); glDeleteShader(shader); @@ -2422,6 +1121,38 @@ static GLuint shader_compile(const char* vert, const char* frag, const char* vp, printf_debug("Program Shader Permut linking error:\nvp: %s; fp: %s\n%s\n", vp, fp, info_log); free_def(info_log); glDeleteProgram(program); + +#if defined(CRE_DEV) + wchar_t temp_buf[MAX_PATH]; + if (SUCCEEDED(SHGetFolderPathW(0, CSIDL_LOCAL_APPDATA, 0, 0, temp_buf))) { + wcscat_s(temp_buf, sizeof(temp_buf) / sizeof(wchar_t), L"\\ReDIVA"); + temp_buf[sizeof(temp_buf) / sizeof(wchar_t) - 1] = 0; + CreateDirectoryW(temp_buf, 0); + + wchar_t buf[MAX_PATH]; + swprintf_s(buf, sizeof(buf) / sizeof(wchar_t), + L"%ls\\shader_error", temp_buf); + buf[sizeof(buf) / sizeof(wchar_t) - 1] = 0; + CreateDirectoryW(buf, 0); + + swprintf_s(buf, sizeof(buf) / sizeof(wchar_t), + L"%ls\\shader_error\\%hs", temp_buf, vp); + buf[sizeof(buf) / sizeof(wchar_t) - 1] = 0; + + file_stream s; + s.open(buf, L"wb"); + s.write(vert, utf8_length(vert)); + s.close(); + + swprintf_s(buf, sizeof(buf) / sizeof(wchar_t), + L"%ls\\shader_error\\%hs", temp_buf, fp); + buf[sizeof(buf) / sizeof(wchar_t) - 1] = 0; + + s.open(buf, L"wb"); + s.write(frag, utf8_length(frag)); + s.close(); + } +#endif return 0; } else { @@ -2457,6 +1188,38 @@ static GLuint shader_compile_binary(const char* vert, const char* frag, const ch printf_debug("Program Shader Permut linking error:\nvp: %s; fp: %s\n%s\n", vp, fp, info_log); free_def(info_log); glDeleteProgram(program); + +#if defined(CRE_DEV) + wchar_t temp_buf[MAX_PATH]; + if (SUCCEEDED(SHGetFolderPathW(0, CSIDL_LOCAL_APPDATA, 0, 0, temp_buf))) { + wcscat_s(temp_buf, sizeof(temp_buf) / sizeof(wchar_t), L"\\ReDIVA"); + temp_buf[sizeof(temp_buf) / sizeof(wchar_t) - 1] = 0; + CreateDirectoryW(temp_buf, 0); + + wchar_t buf[MAX_PATH]; + swprintf_s(buf, sizeof(buf) / sizeof(wchar_t), + L"%ls\\shader_error", temp_buf); + buf[sizeof(buf) / sizeof(wchar_t) - 1] = 0; + CreateDirectoryW(buf, 0); + + swprintf_s(buf, sizeof(buf) / sizeof(wchar_t), + L"%ls\\shader_error\\%hs", temp_buf, vp); + buf[sizeof(buf) / sizeof(wchar_t) - 1] = 0; + + file_stream s; + s.open(buf, L"wb"); + s.write(vert, utf8_length(vert)); + s.close(); + + swprintf_s(buf, sizeof(buf) / sizeof(wchar_t), + L"%ls\\shader_error\\%hs", temp_buf, fp); + buf[sizeof(buf) / sizeof(wchar_t) - 1] = 0; + + s.open(buf, L"wb"); + s.write(frag, utf8_length(frag)); + s.close(); + } +#endif return 0; } else { @@ -2500,77 +1263,20 @@ static void shader_update_data(shader_set_data* set) { if (!set) return; - shader_data& data = set->data; - GLuint current_uniform_index = gl_state.uniform_buffer_index; - GLuint current_uniform = gl_state.uniform_buffer_binding[current_uniform_index]; + shader_opengl_data* opengl_data = set->opengl_data; - bool reset = false; - if (data.state_update_data && data.state_ubo) { - if (GLAD_GL_VERSION_4_5) - glNamedBufferSubData(data.state_ubo, 0, sizeof(data.state), &data.state); - else { - gl_state_bind_uniform_buffer(data.state_ubo); - glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(data.state), &data.state); - reset = true; + if (opengl_data->curr_shader) { + shader_opengl_sub_shader* shader = opengl_data->curr_shader; + if (shader->uniform_val_update) { + glUniform1iv(0, SHADER_MAX_UNIFORM_VALUES, (GLint*)shader->uniform_val); + shader->uniform_val_update = false; } - data.state_update_data = false; } - if (data.state_matrix_update_data && data.state_matrix_ubo) { - if (GLAD_GL_VERSION_4_5) - glNamedBufferSubData(data.state_matrix_ubo, 0, sizeof(data.state_matrix), &data.state_matrix); - else { - gl_state_bind_uniform_buffer(data.state_matrix_ubo); - glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(data.state_matrix), &data.state_matrix); - reset = true; - } - data.state_matrix_update_data = false; - } - - if (data.env_update_data && data.env_ubo) { - if (GLAD_GL_VERSION_4_5) - glNamedBufferSubData(data.env_ubo, 0, sizeof(data.env), &data.env); - else { - gl_state_bind_uniform_buffer(data.env_ubo); - glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(data.env), &data.env); - reset = true; - } - data.env_update_data = false; - } - - if (data.buffer_update_data && data.buffer_ubo) { - if (GLAD_GL_VERSION_4_5) - glNamedBufferSubData(data.buffer_ubo, 0, sizeof(data.buffer.buffer), &data.buffer.buffer); - else { - gl_state_bind_uniform_buffer(data.buffer_ubo); - glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(data.buffer.buffer), &data.buffer.buffer); - reset = true; - } - data.buffer_update_data = false; - } - - if (data.local_update_data && data.local_update_data[0] && data.local) { - glUniform4fv(0, sizeof(data.local->vert) / (sizeof(GLfloat) * 4), (GLfloat*)data.local->vert); - data.local_update_data[0] = false; - } - - if (data.local_update_data && data.local_update_data[1] && data.local) { - glUniform4fv(256, sizeof(data.local->frag) / (sizeof(GLfloat) * 4), (GLfloat*)data.local->frag); - data.local_update_data[1] = false; - } - - if (data.local_uniform_update_data && *data.local_uniform_update_data && data.local_uniform) { - glUniform1iv(512, sizeof(*data.local_uniform) / sizeof(int32_t), (GLint*)data.local_uniform); - *data.local_uniform_update_data = false; - } - - if (reset) - gl_state_bind_uniform_buffer_base(current_uniform_index, current_uniform); - - if (set->primitive_restart) + if (opengl_data->primitive_restart) gl_state_enable_primitive_restart(); else gl_state_disable_primitive_restart(); - gl_state_set_primitive_restart_index(set->primitive_restart_index); + gl_state_set_primitive_restart_index(opengl_data->primitive_restart_index); } diff --git a/src/CRE/shader.hpp b/src/CRE/shader.hpp index 974b35eb..ab1392dc 100644 --- a/src/CRE/shader.hpp +++ b/src/CRE/shader.hpp @@ -11,220 +11,18 @@ #include "../KKdLib/farc.hpp" #include "../KKdLib/mat.hpp" #include "../KKdLib/vec.hpp" +#include "Vulkan/buffer.hpp" #include "static_var.hpp" -#define SHADER_MAX_CLIP_PLANES 8 #define SHADER_MAX_LIGHTS 8 #define SHADER_MAX_PALETTE_MATRICES 32 #define SHADER_MAX_PROGRAM_ENV_PARAMETERS 256 -#define SHADER_MAX_PROGRAM_LOCAL_PARAMETERS 256 +#define SHADER_MAX_PROGRAM_LOCAL_PARAMETERS 64 #define SHADER_MAX_PROGRAM_BUFFER_PARAMETERS 1024 -#define SHADER_MAX_PROGRAM_PARAMETER_BUFFER_SIZE 16384 #define SHADER_MAX_PROGRAM_MATRICES 8 #define SHADER_MAX_TEXTURE_COORDS 8 -#define SHADER_MAX_TEXTURE_IMAGE_UNITS 32 -#define SHADER_MAX_TEXTURE_UNITS 4 #define SHADER_MAX_UNIFORM_BLOCK_SIZE 65536 #define SHADER_MAX_UNIFORM_VALUES 16 -#define SHADER_MAX_VERTEX_UNITS 4 - -struct shader_state_clip { - vec4 plane; - - shader_state_clip(); -}; - -struct shader_state_depth { - vec4 range; - - shader_state_depth(); -}; - -struct shader_state_fog { - vec4 color; - vec4 params; - - shader_state_fog(); -}; - -struct shader_state_light { - vec4 ambient; - vec4 diffuse; - vec4 specular; - vec4 position; - vec4 attenuation; - vec4 spot_direction; - vec4 half; - - shader_state_light(); -}; - -struct shader_state_lightmodel { - vec4 ambient; - vec4 scene_color; - shader_state_lightmodel(); - -}; - -struct shader_state_lightprod { - vec4 ambient; - vec4 diffuse; - vec4 specular; - - shader_state_lightprod(); -}; - -struct shader_state_material { - vec4 ambient; - vec4 diffuse; - vec4 specular; - vec4 emission; - vec4 shininess; - - shader_state_material(); -}; - -struct shader_state_matrix_data { - mat4 mat; - mat4 inv; - mat4 trans; - mat4 invtrans; - - shader_state_matrix_data(); -}; - -struct shader_state_matrix { - shader_state_matrix_data modelview[SHADER_MAX_VERTEX_UNITS]; - shader_state_matrix_data projection; - shader_state_matrix_data mvp; - shader_state_matrix_data texture[SHADER_MAX_TEXTURE_COORDS]; - shader_state_matrix_data palette[SHADER_MAX_PALETTE_MATRICES]; - shader_state_matrix_data program[SHADER_MAX_PROGRAM_MATRICES]; - - shader_state_matrix(); -}; - -struct shader_state_point { - vec4 size; - vec4 attenuation; - - shader_state_point(); -}; - -struct shader_state_texgen { - struct eye { - vec4 s; - vec4 t; - vec4 r; - vec4 q; - - eye(); - } eye; - - struct object { - vec4 s; - vec4 t; - vec4 r; - vec4 q; - - object(); - } object; - - shader_state_texgen(); -}; - -struct shader_state_texenv { - vec4 color; - - shader_state_texenv(); -}; - -struct shader_buffer { - vec4 buffer[SHADER_MAX_PROGRAM_PARAMETER_BUFFER_SIZE / sizeof(vec4)]; - - shader_buffer(); -}; - -struct shader_env { - vec4 frag[SHADER_MAX_PROGRAM_ENV_PARAMETERS]; - vec4 vert[SHADER_MAX_PROGRAM_ENV_PARAMETERS]; - - shader_env(); -}; - -struct shader_state { - shader_state_material material[2]; - shader_state_light light[SHADER_MAX_LIGHTS]; - shader_state_lightmodel lightmodel[2]; - shader_state_lightprod lightprod_front[SHADER_MAX_LIGHTS]; - shader_state_lightprod lightprod_back[SHADER_MAX_LIGHTS]; - shader_state_texgen texgen[SHADER_MAX_TEXTURE_UNITS]; - shader_state_texenv texenv[SHADER_MAX_TEXTURE_UNITS]; - shader_state_fog fog; - shader_state_clip clip[SHADER_MAX_CLIP_PLANES]; - shader_state_point point; - shader_state_depth depth; - - shader_state(); -}; - -struct shader_local; -struct shader_local_uniform; - -struct shader_data { - shader_state state; - shader_state_matrix state_matrix; - shader_env env; - shader_buffer buffer; - shader_local* local; - shader_local_uniform* local_uniform; - bool state_update_data; - bool state_matrix_update_data; - bool env_update_data; - bool buffer_update_data; - bool* local_update_data; - bool* local_uniform_update_data; - GLuint state_ubo; - GLuint state_matrix_ubo; - GLuint env_ubo; - GLuint buffer_ubo; - - shader_data(); -}; - -struct shader; -struct shader_set_data; - -typedef void (*PFNSHADERBINDFUNCPROC)(shader_set_data* set, shader* shad); - -struct shader_bind_func { - uint32_t name_index; - PFNSHADERBINDFUNCPROC bind_func; -}; - -struct shader_local { - vec4 vert[SHADER_MAX_PROGRAM_LOCAL_PARAMETERS]; - vec4 frag[SHADER_MAX_PROGRAM_LOCAL_PARAMETERS]; -}; - -struct shader_local_uniform { - int32_t uniform_val[SHADER_MAX_UNIFORM_VALUES]; -}; - -struct shader_sub_shader { - GLuint program; - shader_local data; - shader_local_uniform data_uniform; - bool data_update[2]; - bool data_uniform_update; -}; - -struct shader_sub { - uint32_t sub_index; - const int32_t* vp_unival_max; - const int32_t* fp_unival_max; - shader_sub_shader* shaders; -}; struct shader_sub_table { uint32_t sub_index; @@ -244,32 +42,127 @@ struct shader_table { const bool* use_permut; }; -struct shader { +struct shader_opengl; +struct shader_vulkan; +struct shader_set_data; + +typedef std::pair shader_vulkan_pair; + +typedef int32_t (*PFNSHADERGETINDEXFUNCPROC)(const char* name); + +typedef void (*PFNSHADEROPENGLBINDFUNCPROC)(shader_set_data* set, shader_opengl* shad); +typedef shader_vulkan_pair(*PFNSHADERVULKANGETFUNCPROC)(shader_set_data* set, shader_vulkan* shad); + +struct shader_opengl_bind_func { + uint32_t name_index; + PFNSHADEROPENGLBINDFUNCPROC bind_func; +}; + +struct shader_vulkan_get_func { + uint32_t name_index; + PFNSHADERVULKANGETFUNCPROC get_func; +}; + +struct shader_opengl_sub_shader { + GLuint program; + int32_t uniform_val[SHADER_MAX_UNIFORM_VALUES]; + bool uniform_val_update; +}; + +struct shader_opengl_sub { + uint32_t sub_index; + const int32_t* vp_unival_max; + const int32_t* fp_unival_max; + shader_opengl_sub_shader* shaders; +}; + +struct shader_opengl { const char* name; uint32_t name_index; int32_t num_sub; - shader_sub* sub; + shader_opengl_sub* sub; int32_t num_uniform; const uniform_name* use_uniform; const bool* use_permut; - PFNSHADERBINDFUNCPROC bind_func; + PFNSHADEROPENGLBINDFUNCPROC bind_func; int32_t bind(shader_set_data* set, uint32_t sub_index); + static bool parse_define(const char* data, char** temp, size_t* temp_size); static bool parse_define(const char* data, int32_t num_uniform, - const int32_t* vp_unival_max, const int32_t* fp_unival_max, int32_t* uniform_value, char** temp, size_t* temp_size); static char* parse_include(char* data, farc* f); static void unbind(); }; -struct shader_set_data { - shader* shaders; +struct shader_opengl_data { size_t size; - shader_data data; + shader_opengl* shaders; + shader_opengl_sub_shader* curr_shader; GLboolean primitive_restart; GLuint primitive_restart_index; + PFNSHADERGETINDEXFUNCPROC get_index_by_name; + + shader_opengl_data(farc* f, bool ignore_cache, + const char* name, const shader_table* shaders_table, const size_t size, + const shader_opengl_bind_func* bind_func_table, const size_t bind_func_table_size, + PFNSHADERGETINDEXFUNCPROC get_index_by_name); + ~shader_opengl_data(); +}; + +struct shader_vulkan_sub_shader { + VkShaderModule shader_module; + int32_t uniform_val[SHADER_MAX_UNIFORM_VALUES]; + bool uniform_val_update; +}; + +struct shader_vulkan_sub { + uint32_t sub_index; + const int32_t* vp_unival_max; + const int32_t* fp_unival_max; + shader_vulkan_sub_shader* vp; + shader_vulkan_sub_shader* fp; +}; + +struct shader_vulkan { + const char* name; + uint32_t name_index; + int32_t num_sub; + shader_vulkan_sub* sub; + int32_t num_uniform; + const uniform_name* use_uniform; + const bool* use_permut; + PFNSHADERVULKANGETFUNCPROC get_func; + + shader_vulkan_pair get(shader_set_data* set, uint32_t sub_index); +}; + +struct shader_vulkan_data { + size_t size; + shader_vulkan* shaders; + shader_vulkan_sub_shader* curr_vp; + shader_vulkan_sub_shader* curr_fp; + VkBool32 primitive_restart; + + int32_t uniform_val[SHADER_MAX_UNIFORM_VALUES]; + + VkDevice device; + VmaAllocator allocator; + + PFNSHADERGETINDEXFUNCPROC get_index_by_name; + + shader_vulkan_data(VkDevice device, VmaAllocator allocator, farc* f, + const shader_table* shaders_table, const size_t size, + const shader_vulkan_get_func* get_func_table, const size_t get_func_table_size, + PFNSHADERGETINDEXFUNCPROC get_index_by_name); + ~shader_vulkan_data(); +}; + +struct shader_set_data { + shader_opengl_data* opengl_data; + shader_vulkan_data* vulkan_data; + shader_set_data(); void disable_primitive_restart(); @@ -280,151 +173,16 @@ struct shader_set_data { GLuint start, GLuint end, GLsizei count, GLenum type, const void* indices); void enable_primitive_restart(); int32_t get_index_by_name(const char* name); - void load(farc* f, bool ignore_cache, const char* name, + std::pair get_vulkan_shader(uint32_t index); + void load_opengl(farc* f, bool ignore_cache, const char* name, const shader_table* shaders_table, const size_t size, - const shader_bind_func* bind_func_table, const size_t bind_func_table_size); - void set(uint32_t index); - void set_primitive_restart_index(GLuint index); - void unload(); - - void buffer_get(size_t index, vec4& data); - void buffer_get(size_t index, size_t count, vec4* data); - void local_frag_get(size_t index, float_t* x, float_t* y, float_t* z, float_t* w); - void local_frag_get(size_t index, vec4& data); - void local_vert_get(size_t index, float_t* x, float_t* y, float_t* z, float_t* w); - void local_vert_get(size_t index, vec4& data); - void env_frag_get(size_t index, float_t* x, float_t* y, float_t* z, float_t* w); - void env_frag_get(size_t index, vec4& data); - void env_frag_get(size_t index, size_t count, vec4* data); - void env_vert_get(size_t index, float_t* x, float_t* y, float_t* z, float_t* w); - void env_vert_get(size_t index, vec4& data); - void env_vert_get(size_t index, size_t count, vec4* data); - void state_clip_get_plane(size_t index, vec4& data); - void state_depth_get_range(vec4& data); - void state_fog_get_color(vec4& data); - void state_fog_get_params(vec4& data); - void state_light_get_ambient(size_t index, vec4& data); - void state_light_get_diffuse(size_t index, vec4& data); - void state_light_get_specular(size_t index, vec4& data); - void state_light_get_position(size_t index, vec4& data); - void state_light_get_attenuation(size_t index, vec4& data); - void state_light_get_spot_direction(size_t index, vec4& data); - void state_light_get_half(size_t index, vec4& data); - void state_lightmodel_get_ambient(bool back, vec4& data); - void state_lightmodel_get_scene_color(bool back, vec4& data); - void state_lightprod_get_ambient(bool back, size_t index, vec4& data); - void state_lightprod_get_diffuse(bool back, size_t index, vec4& data); - void state_lightprod_get_specular(bool back, size_t index, vec4& data); - void state_material_get_ambient(bool back, vec4& data); - void state_material_get_diffuse(bool back, vec4& data); - void state_material_get_specular(bool back, vec4& data); - void state_material_get_emission(bool back, vec4& data); - void state_material_get_shininess(bool back, vec4& data); - void state_matrix_get_modelview(size_t index, mat4& data); - void state_matrix_get_projection(mat4& data); - void state_matrix_get_mvp(mat4& data); - void state_matrix_get_texture(size_t index, mat4& data); - void state_matrix_get_palette(size_t index, mat4& data); - void state_matrix_get_program(size_t index, mat4& data); - void state_point_get_size(vec4& data); - void state_point_get_attenuation(vec4& data); - void state_texgen_get_eye_s(size_t index, vec4& data); - void state_texgen_get_eye_t(size_t index, vec4& data); - void state_texgen_get_eye_r(size_t index, vec4& data); - void state_texgen_get_eye_q(size_t index, vec4& data); - void state_texgen_get_object_s(size_t index, vec4& data); - void state_texgen_get_object_t(size_t index, vec4& data); - void state_texgen_get_object_r(size_t index, vec4& data); - void state_texgen_get_object_q(size_t index, vec4& data); - void state_texenv_get_color(size_t index, vec4& data); - - void buffer_set(size_t index, const vec4& data); - void buffer_set(size_t index, size_t count, const vec4* data); - void local_frag_set(size_t index, float_t x, float_t y, float_t z, float_t w); - void local_frag_set(size_t index, const vec4& data); - void local_frag_set(size_t index, size_t count, const vec4* data); - void local_vert_set(size_t index, float_t x, float_t y, float_t z, float_t w); - void local_vert_set(size_t index, const vec4& data); - void local_vert_set(size_t index, size_t count, const vec4* data); - void env_frag_set(size_t index, float_t x, float_t y, float_t z, float_t w); - void env_frag_set(size_t index, const vec4& data); - void env_frag_set(size_t index, size_t count, const vec4* data); - void env_vert_set(size_t index, float_t x, float_t y, float_t z, float_t w); - void env_vert_set(size_t index, const vec4& data); - void env_vert_set(size_t index, size_t count, const vec4* data); - void state_clip_set_plane(size_t index, float_t x, float_t y, float_t z, float_t w); - void state_clip_set_plane(size_t index, const vec4& data); - void state_depth_set_range(float_t x, float_t y, float_t z, float_t w); - void state_depth_set_range(const vec4& data); - void state_fog_set_color(float_t x, float_t y, float_t z, float_t w); - void state_fog_set_color(const vec4& data); - void state_fog_set_params(float_t x, float_t y, float_t z, float_t w); - void state_fog_set_params(const vec4& data); - void state_light_set_ambient(size_t index, float_t x, float_t y, float_t z, float_t w); - void state_light_set_ambient(size_t index, const vec4& data); - void state_light_set_diffuse(size_t index, float_t x, float_t y, float_t z, float_t w); - void state_light_set_diffuse(size_t index, const vec4& data); - void state_light_set_specular(size_t index, float_t x, float_t y, float_t z, float_t w); - void state_light_set_specular(size_t index, const vec4& data); - void state_light_set_position(size_t index, float_t x, float_t y, float_t z, float_t w); - void state_light_set_position(size_t index, const vec4& data); - void state_light_set_attenuation(size_t index, float_t x, float_t y, float_t z, float_t w); - void state_light_set_attenuation(size_t index, const vec4& data); - void state_light_set_spot_direction(size_t index, float_t x, float_t y, float_t z, float_t w); - void state_light_set_spot_direction(size_t index, const vec4& data); - void state_light_set_half(size_t index, float_t x, float_t y, float_t z, float_t w); - void state_light_set_half(size_t index, const vec4& data); - void state_lightmodel_set_ambient(bool back, float_t x, float_t y, float_t z, float_t w); - void state_lightmodel_set_ambient(bool back, const vec4& data); - void state_lightmodel_set_scene_color(bool back, float_t x, float_t y, float_t z, float_t w); - void state_lightmodel_set_scene_color(bool back, const vec4& data); - void state_lightprod_set_ambient(bool back, size_t index, float_t x, float_t y, float_t z, float_t w); - void state_lightprod_set_ambient(bool back, size_t index, const vec4& data); - void state_lightprod_set_diffuse(bool back, size_t index, float_t x, float_t y, float_t z, float_t w); - void state_lightprod_set_diffuse(bool back, size_t index, const vec4& data); - void state_lightprod_set_specular(bool back, size_t index, float_t x, float_t y, float_t z, float_t w); - void state_lightprod_set_specular(bool back, size_t index, const vec4& data); - void state_material_set_ambient(bool back, float_t x, float_t y, float_t z, float_t w); - void state_material_set_ambient(bool back, const vec4& data); - void state_material_set_diffuse(bool back, float_t x, float_t y, float_t z, float_t w); - void state_material_set_diffuse(bool back, const vec4& data); - void state_material_set_specular(bool back, float_t x, float_t y, float_t z, float_t w); - void state_material_set_specular(bool back, const vec4& data); - void state_material_set_emission(bool back, float_t x, float_t y, float_t z, float_t w); - void state_material_set_emission(bool back, const vec4& data); - void state_material_set_shininess(bool back, float_t x, float_t y, float_t z, float_t w); - void state_material_set_shininess(bool back, const vec4& data); - void state_matrix_set_modelview(const mat4& data, bool mult); - void state_matrix_set_modelview(size_t index, const mat4& data, bool mult); - void state_matrix_set_projection(const mat4& data, bool mult); - void state_matrix_set_mvp(const mat4& data); - void state_matrix_set_modelview(const mat4& model, const mat4& view, bool mult); - void state_matrix_set_modelview(size_t index, const mat4& model, const mat4& view, bool mult); - void state_matrix_set_mvp(const mat4& modelview, const mat4& projection); - void state_matrix_set_mvp(const mat4& model, const mat4& view, const mat4& projection); - void state_matrix_set_texture(size_t index, const mat4& data); - void state_matrix_set_palette(size_t index, const mat4& data); - void state_matrix_set_program(size_t index, const mat4& data); - void state_point_set_size(float_t x, float_t y, float_t z, float_t w); - void state_point_set_size(const vec4& data); - void state_point_set_attenuation(float_t x, float_t y, float_t z, float_t w); - void state_point_set_attenuation(const vec4& data); - void state_texgen_set_eye_s(size_t index, float_t x, float_t y, float_t z, float_t w); - void state_texgen_set_eye_s(size_t index, const vec4& data); - void state_texgen_set_eye_t(size_t index, float_t x, float_t y, float_t z, float_t w); - void state_texgen_set_eye_t(size_t index, const vec4& data); - void state_texgen_set_eye_r(size_t index, float_t x, float_t y, float_t z, float_t w); - void state_texgen_set_eye_r(size_t index, const vec4& data); - void state_texgen_set_eye_q(size_t index, float_t x, float_t y, float_t z, float_t w); - void state_texgen_set_eye_q(size_t index, const vec4& data); - void state_texgen_set_object_s(size_t index, float_t x, float_t y, float_t z, float_t w); - void state_texgen_set_object_s(size_t index, const vec4& data); - void state_texgen_set_object_t(size_t index, float_t x, float_t y, float_t z, float_t w); - void state_texgen_set_object_t(size_t index, const vec4& data); - void state_texgen_set_object_r(size_t index, float_t x, float_t y, float_t z, float_t w); - void state_texgen_set_object_r(size_t index, const vec4& data); - void state_texgen_set_object_q(size_t index, float_t x, float_t y, float_t z, float_t w); - void state_texgen_set_object_q(size_t index, const vec4& data); - void state_texenv_set_color(size_t index, float_t x, float_t y, float_t z, float_t w); - void state_texenv_set_color(size_t index, const vec4& data); + const shader_opengl_bind_func* bind_func_table, const size_t bind_func_table_size, + PFNSHADERGETINDEXFUNCPROC get_index_by_name); + void load_vulkan(VkDevice device, VmaAllocator allocator, farc* f, + const shader_table* shaders_table, const size_t size, + const shader_vulkan_get_func* get_func_table, const size_t get_func_table_size, + PFNSHADERGETINDEXFUNCPROC get_index_by_name); + void set_opengl_shader(uint32_t index); + void unload_opengl(); + void unload_vulkan(); }; diff --git a/src/CRE/shader_ft.cpp b/src/CRE/shader_ft.cpp index dce3aff1..d918cfd8 100644 --- a/src/CRE/shader_ft.cpp +++ b/src/CRE/shader_ft.cpp @@ -4,7 +4,10 @@ */ #include "shader_ft.hpp" +#include "../KKdLib/str_utils.hpp" #include "gl_state.hpp" +#include "random.hpp" +#include "render_context.hpp" enum shader_ft_sub_enum { SHADER_FT_SUB_SHADER_FFP = 0, @@ -19,19 +22,19 @@ enum shader_ft_sub_enum { SHADER_FT_SUB_SSS_FILTER_MIN_NPR, SHADER_FT_SUB_SSS_FILTER_GAUSS_2D, SHADER_FT_SUB_HAIR_DEFAULT, - SHADER_FT_SUB_HAIR_ANISO, + //SHADER_FT_SUB_HAIR_ANISO, SHADER_FT_SUB_HAIR_NPR1, SHADER_FT_SUB_CLOTH_DEFAULT, SHADER_FT_SUB_CLOTH_ANISO, SHADER_FT_SUB_CLOTH_NPR1, SHADER_FT_SUB_TIGHTS, SHADER_FT_SUB_SKY_DEFAULT, - SHADER_FT_SUB_EYE_BALL, - SHADER_FT_SUB_EYE_LENS, + //SHADER_FT_SUB_EYE_BALL, + //SHADER_FT_SUB_EYE_LENS, SHADER_FT_SUB_GLASS_EYE, - SHADER_FT_SUB_MEMBRANE, - SHADER_FT_SUB_SHADOWMAP, - SHADER_FT_SUB_ESM, + //SHADER_FT_SUB_MEMBRANE, + //SHADER_FT_SUB_SHADOWMAP, + //SHADER_FT_SUB_ESM, SHADER_FT_SUB_ESM_GAUSS, //SHADER_FT_SUB_ESM_FILTER, SHADER_FT_SUB_ESM_FILTER_MIN, @@ -41,11 +44,24 @@ enum shader_ft_sub_enum { SHADER_FT_SUB_SILHOUETTE, SHADER_FT_SUB_LAMBERT, SHADER_FT_SUB_CONSTANT, - SHADER_FT_SUB_PEEL, + //SHADER_FT_SUB_PEEL, SHADER_FT_SUB_TONEMAP, SHADER_FT_SUB_TONEMAP_NPR1, - SHADER_FT_SUB_REDUCE_TEX, - SHADER_FT_SUB_MAGNIFY, + //SHADER_FT_SUB_REDUCE_TEX, + SHADER_FT_SUB_GHOST, + SHADER_FT_SUB_REDUCE_TEX_REDUCE_2, + SHADER_FT_SUB_REDUCE_TEX_REDUCE_2_ALPHAMASK, + SHADER_FT_SUB_REDUCE_TEX_REDUCE_4, + SHADER_FT_SUB_REDUCE_TEX_REDUCE_4_EXTRACT, + SHADER_FT_SUB_REDUCE_TEX_REDUCE_COMPOSITE_2, + SHADER_FT_SUB_REDUCE_TEX_REDUCE_COMPOSITE_BLUR, + SHADER_FT_SUB_REDUCE_TEX_REDUCE_COMPOSITE_4, + //SHADER_FT_SUB_MAGNIFY, + SHADER_FT_SUB_MAGNIFY_CONE, + SHADER_FT_SUB_MAGNIFY_CONE2, + SHADER_FT_SUB_MAGNIFY_DIFF, + SHADER_FT_SUB_MAGNIFY_DIFF2, + SHADER_FT_SUB_MAGNIFY_LINEAR, //SHADER_FT_SUB_MLAA, SHADER_FT_SUB_MLAA_EDGE, SHADER_FT_SUB_MLAA_AREA, @@ -53,180 +69,237 @@ enum shader_ft_sub_enum { SHADER_FT_SUB_CONTOUR, SHADER_FT_SUB_CONTOUR_NPR, //SHADER_FT_SUB_EXPOSURE, - SHADER_FT_SUB_EXPOSURE_AVERAGE, - SHADER_FT_SUB_EXPOSURE_MEASURE, SHADER_FT_SUB_EXPOSURE_MINIFY, + SHADER_FT_SUB_EXPOSURE_MEASURE, + SHADER_FT_SUB_EXPOSURE_AVERAGE, //SHADER_FT_SUB_PP_GAUSS, SHADER_FT_SUB_PP_GAUSS_USUAL, SHADER_FT_SUB_PP_GAUSS_CONE, SHADER_FT_SUB_SUN, - SHADER_FT_SUB_FADE, + SHADER_FT_SUB_SUN_NO_TEXTURED, + //SHADER_FT_SUB_FADE, SHADER_FT_SUB_WATER01, - SHADER_FT_SUB_WATER02, - SHADER_FT_SUB_WATER_RING, + //SHADER_FT_SUB_WATER02, + //SHADER_FT_SUB_WATER_RING, SHADER_FT_SUB_WATER_PARTICLE, + //SHADER_FT_SUB_SNOW_PARTICLE, SHADER_FT_SUB_SNOW_PARTICLE, + SHADER_FT_SUB_SNOW_PARTICLE_CPU, SHADER_FT_SUB_LEAF_PARTICLE, SHADER_FT_SUB_STAR, SHADER_FT_SUB_STAR_MILKY_WAY, - SHADER_FT_SUB_SNOW_RING, - SHADER_FT_SUB_SNOW_FOOTPRINT, - SHADER_FT_SUB_SNOW_TEX_SPACE_LIGHT, - SHADER_FT_SUB_SNOW_CALC_NORMAL, + //SHADER_FT_SUB_SNOW_RING, + //SHADER_FT_SUB_SNOW_FOOTPRINT, + //SHADER_FT_SUB_SNOW_TEX_SPACE_LIGHT, + //SHADER_FT_SUB_SNOW_CALC_NORMAL, SHADER_FT_SUB_FLOOR, SHADER_FT_SUB_PUDDLE, SHADER_FT_SUB_SIMPLE_REFLECT, SHADER_FT_SUB_SIMPLE_REFRACT, SHADER_FT_SUB_RIPPLE_EMIT, SHADER_FT_SUB_RAIN, - SHADER_FT_SUB_VOLUME_LIGHT, - SHADER_FT_SUB_FENCE_ALPHA, + //SHADER_FT_SUB_VOLUME_LIGHT, + //SHADER_FT_SUB_FENCE_ALPHA, SHADER_FT_SUB_RIPPLE, SHADER_FT_SUB_FOG_PTCL, SHADER_FT_SUB_PARTICLE, SHADER_FT_SUB_GLITTER_PARTICLE, - SHADER_FT_SUB_SHOW_VECTOR, + //SHADER_FT_SUB_SHOW_VECTOR, SHADER_FT_SUB_FONT, SHADER_FT_SUB_MOVIE, - SHADER_FT_SUB_IMGFILTER, + //SHADER_FT_SUB_IMGFILTER, + SHADER_FT_SUB_BOX4, + SHADER_FT_SUB_BOX8, + SHADER_FT_SUB_COPY, SHADER_FT_SUB_SPRITE, + SHADER_FT_SUB_DOF_RENDER_TILE, // Added + SHADER_FT_SUB_DOF_GATHER_TILE, // Added + SHADER_FT_SUB_DOF_DOWNSAMPLE, // Added + SHADER_FT_SUB_DOF_MAIN_FILTER, // Added + SHADER_FT_SUB_DOF_UPSAMPLE, // Added + SHADER_FT_SUB_GRID, // Added + SHADER_FT_SUB_TRANSPARENCY, // Added SHADER_FT_SUB_SHADER_END, }; static const int32_t blinn_vert_vpt_unival_max[] = { - 1, 1, 0, 1, 0, 0, 0, 3, 1, 1, 0, 0, // 11th added + //1, 1, 0, 1, 0, 0, 0, 3, 1, 1, 0, 0, // 11th added + 1, 0, 1, 0, 0, 0, 3, 1, 1, 0, 0, // 0th removed, 11th added }; static const int32_t blinn_vert_fpt_unival_max[] = { - 0, 0, 2, 1, 0, 1, 1, 3, 0, 0, 1, 1, // 11th added + //0, 0, 2, 1, 0, 1, 1, 3, 0, 0, 1, 1, // 11th added + 0, 2, 1, 0, 1, 1, 3, 0, 0, 1, 1, // 0th removed, 11th added }; static const int32_t blinn_frag_vpt_unival_max[] = { - 1, 1, 0, 1, 0, 0, 0, 3, 1, 1, 0, 0, // 11th added + //1, 1, 0, 1, 0, 0, 0, 3, 1, 1, 0, 0, // 11th added + 1, 0, 1, 0, 0, 0, 3, 1, 1, 0, 0, // 0th removed, 11th added }; static const int32_t blinn_frag_fpt_unival_max[] = { - 0, 0, 2, 1, 1, 1, 1, 3, 0, 0, 1, 1, // 11th added + //0, 0, 2, 1, 1, 1, 1, 3, 0, 0, 1, 1, // 11th added + 0, 2, 1, 1, 1, 1, 3, 0, 0, 1, 1, // 0th removed, 11th added }; static const int32_t item_blinn_vpt_unival_max[] = { - 1, 1, 1, 0, 0, 1, 0, 0, 0, 2, 0, 1, 0, 0, + //1, 1, 1, 0, 0, 1, 0, 0, 0, 2, 0, 1, 0, 0, + 1, 1, 0, 0, 1, 0, 0, 0, 2, 0, 1, 0, 0, // 0th removed }; static const int32_t item_blinn_fpt_unival_max[] = { - 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, + //1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, + 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, // 0th removed }; static const int32_t stage_blinn_vpt_unival_max[] = { - 1, 1, 1, 1, 0, 0, 1, 0, 3, 0, 0, 0, // 11th added + //1, 1, 1, 1, 0, 0, 1, 0, 3, 0, 0, 0, // 11th added + 1, 1, 1, 0, 0, 1, 0, 3, 0, 0, 0, // 0th removed, 11th added }; static const int32_t stage_blinn_fpt_unival_max[] = { - 0, 0, 0, 0, 2, 1, 1, 1, 3, 1, 1, 1, // 11th added + //0, 0, 0, 0, 2, 1, 1, 1, 3, 1, 1, 1, // 11th added + 0, 0, 0, 2, 1, 1, 1, 3, 1, 1, 1, // 0th removed, 11th added }; static const int32_t skin_default_vpt_unival_max[] = { - 1, 1, 1, 0, 2, 0, 1, 0, 0, 0, + //1, 1, 1, 0, 2, 0, 1, 0, 0, 0, + 1, 1, 0, 2, 0, 1, 0, 0, 0, // 0th removed }; static const int32_t skin_default_fpt_unival_max[] = { - 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, + //1, 0, 1, 1, 0, 1, 0, 1, 1, 1, + 0, 1, 1, 0, 1, 0, 1, 1, 1, // 0th removed }; static const int32_t sss_skin_vpt_unival_max[] = { - 1, 1, 0, 0, 0, 1, 0, 0, + //1, 1, 0, 0, 0, 1, 0, 0, + 1, 0, 0, 0, 1, 0, 0, // 0th removed }; static const int32_t sss_skin_fpt_unival_max[] = { - 1, 0, 1, 1, 1, 0, 1, 1, + //1, 0, 1, 1, 1, 0, 1, 1, + 0, 1, 1, 1, 0, 1, 1, // 0th removed }; /*static const int32_t sss_filter_vpt_unival_max[] = { - 0, 3, 0, + //0, 3, 0, + 3, 0, // 0th removed }; static const int32_t sss_filter_fpt_unival_max[] = { - 1, 3, 1, + //1, 3, 1, + 3, 1, // 0th removed };*/ static const int32_t sss_filter_min_vpt_unival_max[] = { - -1, + //0, 0, 0, + 0, 0, // 0th removed }; static const int32_t sss_filter_min_fpt_unival_max[] = { - -1, + //0, 0, 0, + 0, 0, // 0th removed }; static const int32_t sss_filter_min_npr_vpt_unival_max[] = { - -1, + //0, 0, 0, + 0, 0, // 0th removed }; static const int32_t sss_filter_min_npr_fpt_unival_max[] = { - -1, + //0, 0, 0, + 0, 0, // 0th removed }; static const int32_t sss_filter_gauss_2d_vpt_unival_max[] = { - 0, + //0, 0, 0, + 0, 0, // 0th removed }; static const int32_t sss_filter_gauss_2d_fpt_unival_max[] = { - 1, + //1, 0, 0, + 0, 0, // 0th removed }; -static const int32_t hair_default_vpt_unival_max[] = { - 1, 1, 0, 0, 1, 0, 0, 3, 0, 2, 1, 0, 0, 0, 0, // 14th added +/*static const int32_t hair_default_vpt_unival_max[] = { + //1, 1, 0, 0, 1, 0, 0, 3, 0, 2, 1, 0, 0, 0, 0, // 14th added + 1, 0, 0, 1, 0, 0, 3, 0, 2, 1, 0, 0, 0, 0, // 0th removed, 14th added }; static const int32_t hair_default_fpt_unival_max[] = { - 1, 0, 2, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, // 14th added + //1, 0, 2, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, // 14th added + 0, 2, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, // 0th removed, 14th added }; static const int32_t hair_aniso_vpt_unival_max[] = { - 1, 1, 0, 0, 1, 0, 0, 3, 0, 2, 1, 0, 0, 0, 0, // 14th added + //1, 1, 0, 0, 1, 0, 0, 3, 0, 2, 1, 0, 0, 0, 0, // 14th added + 1, 0, 0, 1, 0, 0, 3, 0, 2, 1, 0, 0, 0, 0, // 0th removed, 14th added }; static const int32_t hair_aniso_fpt_unival_max[] = { - 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, // 14th added + //1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, // 14th added + 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, // 0th removed, 14th added +};*/ + +static const int32_t hair_default_vpt_unival_max[] = { + //1, 1, 0, 0, 1, 0, 0, 3, 0, 2, 1, 0, 0, 0, 0, // 14th added + 1, 0, 0, 1, 0, 0, 3, 0, 2, 1, 0, 0, 0, 0, // 0th removed, 14th added +}; + +static const int32_t hair_default_fpt_unival_max[] = { + //1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, // 14th added + 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, // 0th removed, 14th added }; static const int32_t hair_npr1_vpt_unival_max[] = { - 1, 1, 0, 0, 1, 0, 0, 3, 0, 2, 1, 0, 0, 0, 0, // 14th added + //1, 1, 0, 0, 1, 0, 0, 3, 0, 2, 1, 0, 0, 0, 0, // 14th added + 1, 0, 0, 1, 0, 0, 3, 0, 2, 1, 0, 0, 0, 0, // 0th removed, 14th added }; static const int32_t hair_npr1_fpt_unival_max[] = { - 1, 0, 0, 0, 0, 1, 0, 3, 1, 0, 0, 0, 0, 0, 1, // 14th added + //1, 0, 0, 0, 0, 1, 0, 3, 1, 0, 0, 0, 0, 0, 1, // 14th added + 0, 0, 0, 0, 1, 0, 3, 1, 0, 0, 0, 0, 0, 1, // 0th removed, 14th added }; static const int32_t cloth_default_vpt_unival_max[] = { - 1, 1, 0, 0, 1, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0, + //1, 1, 0, 0, 1, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0, + 1, 0, 0, 1, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0, // 0th removed }; static const int32_t cloth_default_fpt_unival_max[] = { - 1, 0, 2, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, + //1, 0, 2, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, + 0, 2, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, // 0th removed }; static const int32_t cloth_aniso_vpt_unival_max[] = { - 1, 1, 0, 0, 1, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0, + //1, 1, 0, 0, 1, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0, + 1, 0, 0, 1, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0, // 0th removed }; static const int32_t cloth_aniso_fpt_unival_max[] = { - 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 3, 0, 0, 1, 1, + //1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 3, 0, 0, 1, 1, + 0, 0, 1, 0, 1, 1, 1, 0, 1, 3, 0, 0, 1, 1, // 0th removed }; static const int32_t cloth_npr1_vpt_unival_max[] = { - 1, 1, 0, 0, 1, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0, + //1, 1, 0, 0, 1, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0, + 1, 0, 0, 1, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0, // 0th removed }; static const int32_t cloth_npr1_fpt_unival_max[] = { - 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, + //1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, // 0th removed }; static const int32_t tights_vpt_unival_max[] = { - 1, 1, 0, 0, 0, 2, 0, 1, 0, 0, 0, + //1, 1, 0, 0, 0, 2, 0, 1, 0, 0, 0, + 1, 0, 0, 0, 2, 0, 1, 0, 0, 0, // 0th removed }; static const int32_t tights_fpt_unival_max[] = { - 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, + //1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, + 1, 1, 1, 0, 1, 0, 1, 1, 1, // 0th removed }; static const int32_t sky_default_vpt_unival_max[] = { @@ -237,53 +310,57 @@ static const int32_t sky_default_fpt_unival_max[] = { 3, 3, 2, 0, 0, 3, 1, // 6th added }; -static const int32_t eye_ball_vpt_unival_max[] = { +/*static const int32_t eye_ball_vpt_unival_max[] = { 1, 0, }; static const int32_t eye_ball_fpt_unival_max[] = { 0, 1, -}; +};*/ -static const int32_t eye_lens_vpt_unival_max[] = { +/*static const int32_t eye_lens_vpt_unival_max[] = { 1, 0, 0, }; static const int32_t eye_lens_fpt_unival_max[] = { 0, 1, 1, -}; +};*/ static const int32_t glass_eye_vpt_unival_max[] = { - 1, 1, 0, 2, 0, 1, 0, 0, + //1, 1, 0, 2, 0, 1, 0, 0, + 1, 0, 2, 0, 1, 0, 0, // 0th removed }; static const int32_t glass_eye_fpt_unival_max[] = { - 1, 1, 1, 0, 1, 0, 1, 1, + //1, 1, 1, 0, 1, 0, 1, 1, + 1, 1, 0, 1, 0, 1, 1, // 0th removed }; -static const int32_t membrane_vpt_unival_max[] = { +/*static const int32_t membrane_vpt_unival_max[] = { 1, 1, 0, }; static const int32_t membrane_fpt_unival_max[] = { 0, 1, 3, -}; +};*/ -static const int32_t shadowmap_vpt_unival_max[] = { +/*static const int32_t shadowmap_vpt_unival_max[] = { 1, 2, 1, 0, 0, }; static const int32_t shadowmap_fpt_unival_max[] = { 0, 2, 1, 1, 5, -}; +};*/ -static const int32_t esm_vpt_unival_max[] = { - 1, 1, 2, 1, +/*static const int32_t esm_vpt_unival_max[] = { + //1, 1, 2, 1, + 1, 2, 1, // 0th removed }; static const int32_t esm_fpt_unival_max[] = { - 0, 0, 2, 1, -}; + //0, 0, 2, 1, + 0, 2, 1, // 0th removed +};*/ static const int32_t esm_gauss_vpt_unival_max[] = { 0, @@ -302,103 +379,217 @@ static const int32_t esm_filter_fpt_unival_max[] = { };*/ static const int32_t esm_filter_min_vpt_unival_max[] = { - -1, + 0, }; static const int32_t esm_filter_min_fpt_unival_max[] = { - -1, + 0, }; static const int32_t esm_filter_erosion_vpt_unival_max[] = { - -1, + 0, }; static const int32_t esm_filter_erosion_fpt_unival_max[] = { - -1, + 0, }; static const int32_t lit_proj_vpt_unival_max[] = { - 1, 1, 0, 1, 0, 0, + //1, 1, 0, 1, 0, 0, + 1, 0, 1, 0, 0, // 0th removed }; static const int32_t lit_proj_fpt_unival_max[] = { - 0, 0, 1, 1, 2, 1, + //0, 0, 1, 1, 2, 1, + 0, 1, 1, 2, 1, // 0th removed }; static const int32_t simple_vpt_unival_max[] = { - 1, 1, 0, 0, 1, 0, 0, // 6th added + //1, 1, 0, 0, 1, 0, 0, // 6th added + 1, 0, 0, // 0th removed, 3rd removed, 4th removed, 5th removed, 6th added }; static const int32_t simple_fpt_unival_max[] = { - 0, 0, 2, 0, 0, 3, 1, // 6th added + //0, 0, 2, 0, 0, 3, 1, // 6th added + 0, 2, 1, // 0th removed, 3rd removed, 4th removed, 5th removed, 6th added }; static const int32_t silhouette_vpt_unival_max[] = { - 1, 1, 1, 2, 1, 1, 0, 0, // 7th added + //1, 1, 1, 2, 1, 1, 0, 0, // 7th added + 1, 1, 2, 1, 1, 0, 0, // 0th removed, 7th added }; static const int32_t silhouette_fpt_unival_max[] = { - 0, 0, 1, 0, 0, 0, 1, 1, // 7th added + //0, 0, 1, 0, 0, 0, 1, 1, // 7th added + 0, 1, 0, 0, 0, 1, 1, // 0th removed, 7th added }; static const int32_t lambert_vpt_unival_max[] = { - 1, 1, 0, 1, 3, 1, 1, 0, 0, // 8th added + //1, 1, 0, 1, 3, 1, 1, 0, 0, // 8th added + 1, 0, 1, 3, 1, 1, 0, 0, // 0th removed, 8th added }; static const int32_t lambert_fpt_unival_max[] = { - 0, 0, 2, 1, 3, 0, 0, 1, 1, // 8th added + //0, 0, 2, 1, 3, 0, 0, 1, 1, // 8th added + 0, 2, 1, 3, 0, 0, 1, 1, // 0th removed, 8th added }; static const int32_t constant_vpt_unival_max[] = { - 1, 1, 0, 3, 1, 1, 0, // 6th added + //1, 1, 0, 3, 1, 1, 0, // 6th added + 1, 0, 3, 1, 1, 0, // 0th removed, 6th added }; static const int32_t constant_fpt_unival_max[] = { - 0, 0, 2, 3, 0, 0, 1, // 6th added + //0, 0, 2, 3, 0, 0, 1, // 6th added + 0, 2, 3, 0, 0, 1, // 0th removed, 6th added }; -static const int32_t peel_vpt_unival_max[] = { +/*static const int32_t peel_vpt_unival_max[] = { 0, 1, }; static const int32_t peel_fpt_unival_max[] = { 1, 0, -}; +};*/ -static const int32_t tonemap_vpt_unival_max[] = { +static const int32_t tone_map_vpt_unival_max[] = { //0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, // 7th removed + 0, 0, 0, 1, 0, 0, // 0th, 7th removed }; -static const int32_t tonemap_fpt_unival_max[] = { +static const int32_t tone_map_fpt_unival_max[] = { //0, 2, 2, 1, 1, 1, 0, 0, - 0, 2, 2, 1, 1, 1, 0, // 7th removed + 2, 2, 1, 1, 1, 0, // 0th, 7th removed }; -static const int32_t tonemap_npr1_vpt_unival_max[] = { +static const int32_t tone_map_npr1_vpt_unival_max[] = { //0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 1, 0, 0, // 7th removed + 0, 0, 0, 1, 0, 0, // 0th, 7th removed }; -static const int32_t tonemap_npr1_fpt_unival_max[] = { +static const int32_t tone_map_npr1_fpt_unival_max[] = { //1, 0, 0, 1, 1, 0, 0, 1, - 1, 0, 0, 1, 1, 0, 0, // 7th removed + 0, 0, 1, 1, 0, 0, // 0th, 7th removed }; -static const int32_t reduce_tex_vpt_unival_max[] = { +/*static const int32_t reduce_tex_vpt_unival_max[] = { 8, 1, }; static const int32_t reduce_tex_fpt_unival_max[] = { 8, 1, +};*/ + +static const int32_t reduce_tex_reduce_2_vpt_unival_max[] = { + 0, 0, }; -static const int32_t magnify_vpt_unival_max[] = { +static const int32_t reduce_tex_reduce_2_fpt_unival_max[] = { + 0, 0, +}; + +static const int32_t reduce_tex_reduce_2_alphamask_vpt_unival_max[] = { + 0, 0, +}; + +static const int32_t reduce_tex_reduce_2_alphamask_fpt_unival_max[] = { + 0, 0, +}; + +static const int32_t reduce_tex_reduce_4_vpt_unival_max[] = { + 0, 0, +}; + +static const int32_t reduce_tex_reduce_4_fpt_unival_max[] = { + 0, 0, +}; + +static const int32_t reduce_tex_reduce_4_extract_vpt_unival_max[] = { + 0, 0, +}; + +static const int32_t reduce_tex_reduce_4_extract_fpt_unival_max[] = { + 0, 0, +}; + +static const int32_t ghost_vpt_unival_max[] = { + 0, 0, +}; + +static const int32_t ghost_fpt_unival_max[] = { + 0, 0, +}; + +static const int32_t reduce_tex_reduce_composite_2_vpt_unival_max[] = { + 0, 0, +}; + +static const int32_t reduce_tex_reduce_composite_2_fpt_unival_max[] = { + 0, 0, +}; + +static const int32_t reduce_tex_reduce_composite_blur_vpt_unival_max[] = { + 0, 0, +}; + +static const int32_t reduce_tex_reduce_composite_blur_fpt_unival_max[] = { + 0, 0, +}; + +static const int32_t reduce_tex_reduce_composite_4_vpt_unival_max[] = { + 0, 0, +}; + +static const int32_t reduce_tex_reduce_composite_4_fpt_unival_max[] = { + 0, 0, +}; + +/*static const int32_t magnify_vpt_unival_max[] = { 7, }; static const int32_t magnify_fpt_unival_max[] = { 7, +};*/ + +static const int32_t magnify_linear_vpt_unival_max[] = { + 0, +}; + +static const int32_t magnify_linear_fpt_unival_max[] = { + 0, +}; + +static const int32_t magnify_diff_vpt_unival_max[] = { + 0, +}; + +static const int32_t magnify_diff_fpt_unival_max[] = { + 0, +}; + +static const int32_t magnify_diff2_vpt_unival_max[] = { + 0, +}; + +static const int32_t magnify_diff2_fpt_unival_max[] = { + 0, +}; + +static const int32_t magnify_cone_vpt_unival_max[] = { + 0, +}; + +static const int32_t magnify_cone_fpt_unival_max[] = { + 0, +}; + +static const int32_t magnify_cone2_vpt_unival_max[] = { + 0, +}; + +static const int32_t magnify_cone2_fpt_unival_max[] = { + 0, }; /*static const int32_t mlaa_vpt_unival_max[] = { @@ -412,35 +603,37 @@ static const int32_t mlaa_fpt_unival_max[] = { };*/ static const int32_t mlaa_edge_vpt_unival_max[] = { - 0, + 0, 0, 0, }; static const int32_t mlaa_edge_fpt_unival_max[] = { - 1, + 1, 0, 0, }; static const int32_t mlaa_area_vpt_unival_max[] = { - 0, + 0, 0, 0, }; static const int32_t mlaa_area_fpt_unival_max[] = { - 2, + 0, 0, 2, }; static const int32_t mlaa_blend_vpt_unival_max[] = { - 0, + 0, 0, 0, }; static const int32_t mlaa_blend_fpt_unival_max[] = { - 1, + 1, 0, 0, }; static const int32_t contour_vpt_unival_max[] = { - 0, 1, + //0, 1, + 1, // 0th removed }; static const int32_t contour_fpt_unival_max[] = { - 1, 1, + //1, 1, + 1, // 0th removed }; static const int32_t contour_npr_vpt_unival_max[] = { @@ -460,27 +653,27 @@ static const int32_t exposure_fpt_unival_max[] = { };*/ static const int32_t exposure_minify_vpt_unival_max[] = { - -1, + 0, }; static const int32_t exposure_minify_fpt_unival_max[] = { - -1, + 0, }; static const int32_t exposure_measure_vpt_unival_max[] = { - -1, + 0, }; static const int32_t exposure_measure_fpt_unival_max[] = { - -1, + 0, }; static const int32_t exposure_average_vpt_unival_max[] = { - -1, + 0, }; static const int32_t exposure_average_fpt_unival_max[] = { - -1, + 0, }; /*static const int32_t pp_gauss_vpt_unival_max[] = { @@ -492,19 +685,19 @@ static const int32_t pp_gauss_fpt_unival_max[] = { };*/ static const int32_t pp_gauss_usual_vpt_unival_max[] = { - -1, + 0, }; static const int32_t pp_gauss_usual_fpt_unival_max[] = { - -1, + 0, }; static const int32_t pp_gauss_cone_vpt_unival_max[] = { - -1, + 0, }; static const int32_t pp_gauss_cone_fpt_unival_max[] = { - -1, + 0, }; static const int32_t sun_vpt_unival_max[] = { @@ -515,13 +708,21 @@ static const int32_t sun_fpt_unival_max[] = { -1, }; -static const int32_t fade_vpt_unival_max[] = { +static const int32_t sun_no_textured_vpt_unival_max[] = { + -1, +}; + +static const int32_t sun_no_textured_fpt_unival_max[] = { + -1, +}; + +/*static const int32_t fade_vpt_unival_max[] = { 5, }; static const int32_t fade_fpt_unival_max[] = { 5, -}; +};*/ static const int32_t water01_vpt_unival_max[] = { 1, 1, 0, 0, 0, 0, 0, 1, 0, // 8th added @@ -531,21 +732,21 @@ static const int32_t water01_fpt_unival_max[] = { 0, 0, 2, 1, 1, 1, 1, 1, 1, // 8th added }; -static const int32_t water02_vpt_unival_max[] = { +/*static const int32_t water02_vpt_unival_max[] = { -1, }; static const int32_t water02_fpt_unival_max[] = { -1, -}; +};*/ -static const int32_t water_ring_vpt_unival_max[] = { +/*static const int32_t water_ring_vpt_unival_max[] = { 1, 0, }; static const int32_t water_ring_fpt_unival_max[] = { 1, 1, -}; +};*/ static const int32_t water_particle_vpt_unival_max[] = { 0, // 0th added @@ -555,12 +756,28 @@ static const int32_t water_particle_fpt_unival_max[] = { 0, // 0th added }; -static const int32_t snow_particle_vpt_unival_max[] = { +/*static const int32_t snow_particle_vpt_unival_max[] = { 1, }; static const int32_t snow_particle_fpt_unival_max[] = { 1, +};*/ + +static const int32_t snow_particle_vpt_unival_max[] = { + 0, +}; + +static const int32_t snow_particle_fpt_unival_max[] = { + 0, +}; + +static const int32_t snow_particle_cpu_vpt_unival_max[] = { + 0, +}; + +static const int32_t snow_particle_cpu_fpt_unival_max[] = { + 0, }; static const int32_t leaf_particle_vpt_unival_max[] = { @@ -580,19 +797,19 @@ static const int32_t star_fpt_unival_max[] = { };*/ static const int32_t star_vpt_unival_max[] = { - -1, + 0, }; static const int32_t star_fpt_unival_max[] = { - -1, + 0, }; static const int32_t star_milky_way_vpt_unival_max[] = { - -1, + 0, }; static const int32_t star_milky_way_fpt_unival_max[] = { - -1, + 0, }; static const int32_t snow_ring_vpt_unival_max[] = { @@ -644,11 +861,13 @@ static const int32_t puddle_fpt_unival_max[] = { }; static const int32_t simple_reflect_vpt_unival_max[] = { - 1, 1, 2, 1, 1, 0, 0, 0, 0, 0, + //1, 1, 2, 1, 1, 0, 0, 0, 0, 0, + 1, 2, 1, 1, 0, 0, 0, 0, 0, // 0th removed }; static const int32_t simple_reflect_fpt_unival_max[] = { - 0, 0, 2, 0, 0, 1, 1, 2, 1, 1, + //0, 0, 2, 0, 0, 1, 1, 2, 1, 1, + 0, 2, 0, 0, 1, 1, 2, 1, 1, // 0th removed }; static const int32_t simple_refract_vpt_unival_max[] = { @@ -675,21 +894,21 @@ static const int32_t rain_fpt_unival_max[] = { -1, }; -static const int32_t volume_light_vpt_unival_max[] = { +/*static const int32_t volume_light_vpt_unival_max[] = { -1, }; static const int32_t volume_light_fpt_unival_max[] = { -1, -}; +};*/ -static const int32_t fence_alpha_vpt_unival_max[] = { +/*static const int32_t fence_alpha_vpt_unival_max[] = { 1, 3, }; static const int32_t fence_alpha_fpt_unival_max[] = { 0, 3, -}; +};*/ static const int32_t ripple_vpt_unival_max[] = { 0, @@ -723,20 +942,24 @@ static const int32_t glitter_particle_fpt_unival_max[] = { 2, 3, 3, 3, }; -static const int32_t show_vector_vpt_unival_max[] = { - 1, 1, 3, +/*static const int32_t show_vector_vpt_unival_max[] = { + //1, 1, 3, + 1, 3, // 0th removed }; static const int32_t show_vector_fpt_unival_max[] = { - 0, 0, 0, -}; + //0, 0, 0, + 0, 0, // 0th removed +};*/ static const int32_t font_vpt_unival_max[] = { - 0, + //0, + -1, // 0th removed, 1st added }; static const int32_t font_fpt_unival_max[] = { - 1, + //1, + -1, // 0th removed, 1st added }; static const int32_t movie_vpt_unival_max[] = { @@ -747,14 +970,37 @@ static const int32_t movie_fpt_unival_max[] = { 1, }; -static const int32_t imgfilter_vpt_unival_max[] = { +/*static const int32_t imgfilter_vpt_unival_max[] = { 0, }; static const int32_t imgfilter_fpt_unival_max[] = { 5, +};*/ + +static const int32_t box4_vpt_unival_max[] = { + 0, }; +static const int32_t box4_fpt_unival_max[] = { + 0, +}; + +static const int32_t box8_vpt_unival_max[] = { + 0, +}; + +static const int32_t box8_fpt_unival_max[] = { + 0, +}; + +static const int32_t copy_vpt_unival_max[] = { + 0, +}; + +static const int32_t copy_fpt_unival_max[] = { + 0, +}; static const int32_t sprite_vpt_unival_max[] = { 0, 0, 0, }; @@ -763,6 +1009,62 @@ static const int32_t sprite_fpt_unival_max[] = { 3, 3, 2, }; +static const int32_t dof_render_tile_vpt_unival_max[] = { // Added + 0, +}; + +static const int32_t dof_render_tile_fpt_unival_max[] = { // Added + 1, +}; + +static const int32_t dof_gather_tile_vpt_unival_max[] = { // Added + 0, +}; + +static const int32_t dof_gather_tile_fpt_unival_max[] = { // Added + 0, +}; + +static const int32_t dof_downsample_vpt_unival_max[] = { // Added + 0, +}; + +static const int32_t dof_downsample_fpt_unival_max[] = { // Added + 1, +}; + +static const int32_t dof_main_filter_vpt_unival_max[] = { // Added + 0, +}; + +static const int32_t dof_main_filter_fpt_unival_max[] = { // Added + 1, +}; + +static const int32_t dof_upsample_vpt_unival_max[] = { // Added + 0, +}; + +static const int32_t dof_upsample_fpt_unival_max[] = { // Added + 1, +}; + +static const int32_t grid_vpt_unival_max[] = { // Added + -1, +}; + +static const int32_t grid_fpt_unival_max[] = { // Added + -1, +}; + +static const int32_t transparency_vpt_unival_max[] = { // Added + -1, +}; + +static const int32_t transparency_fpt_unival_max[] = { // Added + -1, +}; + static const shader_sub_table BLINN_table[] = { { SHADER_FT_SUB_BLINN_VERT, @@ -830,7 +1132,7 @@ static const shader_sub_table SSS_SKIN_table[] = { }, };*/ -static const shader_sub_table SSS_FILT_MIN_table[] = { +static const shader_sub_table SSS_FILT_table[] = { { SHADER_FT_SUB_SSS_FILTER_MIN, sss_filter_min_vpt_unival_max, @@ -845,9 +1147,6 @@ static const shader_sub_table SSS_FILT_MIN_table[] = { "sss_filter_min", "sss_filter_min_npr", }, -}; - -static const shader_sub_table SSS_FILT_GAUSS_2D_table[] = { { SHADER_FT_SUB_SSS_FILTER_GAUSS_2D, sss_filter_gauss_2d_vpt_unival_max, @@ -857,7 +1156,7 @@ static const shader_sub_table SSS_FILT_GAUSS_2D_table[] = { }, }; -static const shader_sub_table HAIR_table[] = { +/*static const shader_sub_table HAIR_table[] = { { SHADER_FT_SUB_HAIR_DEFAULT, hair_default_vpt_unival_max, @@ -879,6 +1178,23 @@ static const shader_sub_table HAIR_table[] = { "hair_default", "hair_npr1", }, +};*/ + +static const shader_sub_table HAIR_table[] = { + { + SHADER_FT_SUB_HAIR_DEFAULT, + hair_default_vpt_unival_max, + hair_default_fpt_unival_max, + "hair_default", + "hair_default", + }, + { + SHADER_FT_SUB_HAIR_NPR1, + hair_npr1_vpt_unival_max, + hair_npr1_fpt_unival_max, + "hair_default", + "hair_npr1", + }, }; static const shader_sub_table CLOTH_table[] = { @@ -925,7 +1241,7 @@ static const shader_sub_table SKY_table[] = { }, }; -static const shader_sub_table EYEBALL_table[] = { +/*static const shader_sub_table EYEBALL_table[] = { { SHADER_FT_SUB_EYE_BALL, eye_ball_vpt_unival_max, @@ -933,9 +1249,9 @@ static const shader_sub_table EYEBALL_table[] = { "eye_ball", "eye_ball", }, -}; +};*/ -static const shader_sub_table EYELENS_table[] = { +/*static const shader_sub_table EYELENS_table[] = { { SHADER_FT_SUB_EYE_LENS, eye_lens_vpt_unival_max, @@ -943,7 +1259,7 @@ static const shader_sub_table EYELENS_table[] = { "eye_lens", "eye_lens", }, -}; +};*/ static const shader_sub_table GLASEYE_table[] = { { @@ -955,7 +1271,7 @@ static const shader_sub_table GLASEYE_table[] = { }, }; -static const shader_sub_table MEMBRAN_table[] = { +/*static const shader_sub_table MEMBRAN_table[] = { { SHADER_FT_SUB_MEMBRANE, membrane_vpt_unival_max, @@ -963,9 +1279,9 @@ static const shader_sub_table MEMBRAN_table[] = { "membrane", "membrane", }, -}; +};*/ -static const shader_sub_table SHDMAP_table[] = { +/*static const shader_sub_table SHDMAP_table[] = { { SHADER_FT_SUB_SHADOWMAP, shadowmap_vpt_unival_max, @@ -973,9 +1289,9 @@ static const shader_sub_table SHDMAP_table[] = { "shadowmap", "shadowmap", }, -}; +};*/ -static const shader_sub_table ESM_table[] = { +/*static const shader_sub_table ESM_table[] = { { SHADER_FT_SUB_ESM, esm_vpt_unival_max, @@ -983,7 +1299,7 @@ static const shader_sub_table ESM_table[] = { "esm", "esm", }, -}; +};*/ static const shader_sub_table ESMGAUSS_table[] = { { @@ -1005,7 +1321,7 @@ static const shader_sub_table ESMGAUSS_table[] = { }, };*/ -static const shader_sub_table ESMFILT_MIN_table[] = { +static const shader_sub_table ESMFILT_table[] = { { SHADER_FT_SUB_ESM_FILTER_MIN, esm_filter_min_vpt_unival_max, @@ -1013,9 +1329,6 @@ static const shader_sub_table ESMFILT_MIN_table[] = { "esm_filter_min", "esm_filter_min", }, -}; - -static const shader_sub_table ESMFILT_EROSION_table[] = { { SHADER_FT_SUB_ESM_FILTER_EROSION, esm_filter_erosion_vpt_unival_max, @@ -1075,7 +1388,7 @@ static const shader_sub_table CONSTANT_table[] = { }, }; -static const shader_sub_table PEEL_table[] = { +/*static const shader_sub_table PEEL_table[] = { { SHADER_FT_SUB_PEEL, peel_vpt_unival_max, @@ -1083,26 +1396,26 @@ static const shader_sub_table PEEL_table[] = { "depth_peel", "depth_peel", }, -}; +};*/ static const shader_sub_table TONEMAP_table[] = { { SHADER_FT_SUB_TONEMAP, - tonemap_vpt_unival_max, - tonemap_fpt_unival_max, + tone_map_vpt_unival_max, + tone_map_fpt_unival_max, "tone_map", "tone_map", }, { SHADER_FT_SUB_TONEMAP_NPR1, - tonemap_npr1_vpt_unival_max, - tonemap_npr1_fpt_unival_max, - "tone_map", + tone_map_npr1_vpt_unival_max, + tone_map_npr1_fpt_unival_max, + "tone_map_npr1", "tone_map_npr1", }, }; -static const shader_sub_table REDUCE_table[] = { +/*static const shader_sub_table REDUCE_table[] = { { SHADER_FT_SUB_REDUCE_TEX, reduce_tex_vpt_unival_max, @@ -1110,9 +1423,68 @@ static const shader_sub_table REDUCE_table[] = { "reduce_tex", "reduce_tex", }, +};*/ + +static const shader_sub_table REDUCE_table[] = { + { + SHADER_FT_SUB_REDUCE_TEX_REDUCE_2, + reduce_tex_reduce_2_vpt_unival_max, + reduce_tex_reduce_2_fpt_unival_max, + "reduce_tex_reduce_2", + "reduce_tex_reduce_2", + }, + { + SHADER_FT_SUB_REDUCE_TEX_REDUCE_2_ALPHAMASK, + reduce_tex_reduce_2_alphamask_vpt_unival_max, + reduce_tex_reduce_2_alphamask_fpt_unival_max, + "reduce_tex_reduce_2_alphamask", + "reduce_tex_reduce_2_alphamask", + }, + { + SHADER_FT_SUB_REDUCE_TEX_REDUCE_4, + reduce_tex_reduce_4_vpt_unival_max, + reduce_tex_reduce_4_fpt_unival_max, + "reduce_tex_reduce_4", + "reduce_tex_reduce_4", + }, + { + SHADER_FT_SUB_REDUCE_TEX_REDUCE_4_EXTRACT, + reduce_tex_reduce_4_extract_vpt_unival_max, + reduce_tex_reduce_4_extract_fpt_unival_max, + "reduce_tex_reduce_4_extract", + "reduce_tex_reduce_4_extract", + }, + { + SHADER_FT_SUB_GHOST, + ghost_vpt_unival_max, + ghost_fpt_unival_max, + "ghost", + "ghost", + }, + { + SHADER_FT_SUB_REDUCE_TEX_REDUCE_COMPOSITE_2, + reduce_tex_reduce_composite_2_vpt_unival_max, + reduce_tex_reduce_composite_2_fpt_unival_max, + "reduce_tex_reduce_composite_2", + "reduce_tex_reduce_composite_2", + }, + { + SHADER_FT_SUB_REDUCE_TEX_REDUCE_COMPOSITE_BLUR, + reduce_tex_reduce_composite_blur_vpt_unival_max, + reduce_tex_reduce_composite_blur_fpt_unival_max, + "reduce_tex_reduce_composite_blur", + "reduce_tex_reduce_composite_blur", + }, + { + SHADER_FT_SUB_REDUCE_TEX_REDUCE_COMPOSITE_4, + reduce_tex_reduce_composite_4_vpt_unival_max, + reduce_tex_reduce_composite_4_fpt_unival_max, + "reduce_tex_reduce_composite_4", + "reduce_tex_reduce_composite_4", + }, }; -static const shader_sub_table MAGNIFY_table[] = { +/*static const shader_sub_table MAGNIFY_table[] = { { SHADER_FT_SUB_MAGNIFY, magnify_vpt_unival_max, @@ -1120,6 +1492,44 @@ static const shader_sub_table MAGNIFY_table[] = { "magnify", "magnify", }, +};*/ + +static const shader_sub_table MAGNIFY_table[] = { + { + SHADER_FT_SUB_MAGNIFY_LINEAR, + magnify_linear_vpt_unival_max, + magnify_linear_fpt_unival_max, + "magnify_linear", + "magnify_linear", + }, + { + SHADER_FT_SUB_MAGNIFY_DIFF, + magnify_diff_vpt_unival_max, + magnify_diff_fpt_unival_max, + "magnify_diff", + "magnify_diff", + }, + { + SHADER_FT_SUB_MAGNIFY_DIFF2, + magnify_diff2_vpt_unival_max, + magnify_diff2_fpt_unival_max, + "magnify_diff2", + "magnify_diff2", + }, + { + SHADER_FT_SUB_MAGNIFY_CONE, + magnify_cone_vpt_unival_max, + magnify_cone_fpt_unival_max, + "magnify_cone", + "magnify_cone", + }, + { + SHADER_FT_SUB_MAGNIFY_CONE2, + magnify_cone2_vpt_unival_max, + magnify_cone2_fpt_unival_max, + "magnify_cone2", + "magnify_cone2", + }, }; /*static const shader_sub_table MLAA_table[] = { @@ -1132,7 +1542,7 @@ static const shader_sub_table MAGNIFY_table[] = { }, };*/ -static const shader_sub_table MLAA_EDGE_table[] = { +static const shader_sub_table MLAA_table[] = { { SHADER_FT_SUB_MLAA_EDGE, mlaa_edge_vpt_unival_max, @@ -1140,9 +1550,6 @@ static const shader_sub_table MLAA_EDGE_table[] = { "mlaa_edge", "mlaa_edge", }, -}; - -static const shader_sub_table MLAA_AREA_table[] = { { SHADER_FT_SUB_MLAA_AREA, mlaa_area_vpt_unival_max, @@ -1150,9 +1557,6 @@ static const shader_sub_table MLAA_AREA_table[] = { "mlaa_area", "mlaa_area", }, -}; - -static const shader_sub_table MLAA_BLEND_table[] = { { SHADER_FT_SUB_MLAA_BLEND, mlaa_blend_vpt_unival_max, @@ -1192,7 +1596,7 @@ static const shader_sub_table CONTOUR_NPR_table[] = { }, };*/ -static const shader_sub_table EXPOSURE_MINIFY_table[] = { +static const shader_sub_table EXPOSURE_table[] = { { SHADER_FT_SUB_EXPOSURE_MINIFY, exposure_minify_vpt_unival_max, @@ -1200,9 +1604,6 @@ static const shader_sub_table EXPOSURE_MINIFY_table[] = { "exposure_minify", "exposure_minify", }, -}; - -static const shader_sub_table EXPOSURE_MEASURE_table[] = { { SHADER_FT_SUB_EXPOSURE_MEASURE, exposure_measure_vpt_unival_max, @@ -1210,9 +1611,6 @@ static const shader_sub_table EXPOSURE_MEASURE_table[] = { "exposure_measure", "exposure_measure", }, -}; - -static const shader_sub_table EXPOSURE_AVERAGE_table[] = { { SHADER_FT_SUB_EXPOSURE_AVERAGE, exposure_average_vpt_unival_max, @@ -1232,7 +1630,7 @@ static const shader_sub_table EXPOSURE_AVERAGE_table[] = { }, };*/ -static const shader_sub_table GAUSS_USUAL_table[] = { +static const shader_sub_table GAUSS_table[] = { { SHADER_FT_SUB_PP_GAUSS_USUAL, pp_gauss_usual_vpt_unival_max, @@ -1240,9 +1638,6 @@ static const shader_sub_table GAUSS_USUAL_table[] = { "pp_gauss_usual", "pp_gauss_usual", }, -}; - -static const shader_sub_table GAUSS_CONE_table[] = { { SHADER_FT_SUB_PP_GAUSS_CONE, pp_gauss_cone_vpt_unival_max, @@ -1262,7 +1657,17 @@ static const shader_sub_table SUN_table[] = { }, }; -static const shader_sub_table FADE_table[] = { +static const shader_sub_table SUN_NO_TEXTURED_table[] = { + { + SHADER_FT_SUB_SUN_NO_TEXTURED, + sun_no_textured_vpt_unival_max, + sun_no_textured_fpt_unival_max, + "sun_no_textured", + "sun_no_textured", + }, +}; + +/*static const shader_sub_table FADE_table[] = { { SHADER_FT_SUB_FADE, fade_vpt_unival_max, @@ -1270,7 +1675,7 @@ static const shader_sub_table FADE_table[] = { "fade", "fade", }, -}; +};*/ static const shader_sub_table WATER01_table[] = { { @@ -1282,7 +1687,7 @@ static const shader_sub_table WATER01_table[] = { }, }; -static const shader_sub_table WATER02_table[] = { +/*static const shader_sub_table WATER02_table[] = { { SHADER_FT_SUB_WATER02, water02_vpt_unival_max, @@ -1290,9 +1695,9 @@ static const shader_sub_table WATER02_table[] = { "water02", "water02", }, -}; +};*/ -static const shader_sub_table WATRING_table[] = { +/*static const shader_sub_table WATRING_table[] = { { SHADER_FT_SUB_WATER_RING, water_ring_vpt_unival_max, @@ -1300,7 +1705,7 @@ static const shader_sub_table WATRING_table[] = { "water_ring", "water_ring", }, -}; +};*/ static const shader_sub_table W_PTCL_table[] = { { @@ -1312,6 +1717,16 @@ static const shader_sub_table W_PTCL_table[] = { }, }; +/*static const shader_sub_table SNOW_PT_table[] = { + { + SHADER_FT_SUB_SNOW_PARTICLE, + snow_particle_vpt_unival_max, + snow_particle_fpt_unival_max, + "snow_particle", + "snow_particle", + }, +};*/ + static const shader_sub_table SNOW_PT_table[] = { { SHADER_FT_SUB_SNOW_PARTICLE, @@ -1320,6 +1735,13 @@ static const shader_sub_table SNOW_PT_table[] = { "snow_particle", "snow_particle", }, + { + SHADER_FT_SUB_SNOW_PARTICLE_CPU, + snow_particle_cpu_vpt_unival_max, + snow_particle_cpu_fpt_unival_max, + "snow_particle_cpu", + "snow_particle_cpu", + }, }; static const shader_sub_table LEAF_PT_table[] = { @@ -1340,9 +1762,6 @@ static const shader_sub_table STAR_table[] = { "star", "star", }, -}; - -static const shader_sub_table STAR_MILKY_WAY_table[] = { { SHADER_FT_SUB_STAR_MILKY_WAY, star_milky_way_vpt_unival_max, @@ -1352,7 +1771,7 @@ static const shader_sub_table STAR_MILKY_WAY_table[] = { }, }; -static const shader_sub_table SNORING_table[] = { +/*static const shader_sub_table SNORING_table[] = { { SHADER_FT_SUB_SNOW_RING, snow_ring_vpt_unival_max, @@ -1390,7 +1809,7 @@ static const shader_sub_table SN_NRM_table[] = { "snow_calc_normal", "snow_calc_normal", }, -}; +};*/ static const shader_sub_table FLOOR_table[] = { { @@ -1452,7 +1871,7 @@ static const shader_sub_table RAIN_table[] = { }, }; -static const shader_sub_table VOLLIT_table[] = { +/*static const shader_sub_table VOLLIT_table[] = { { SHADER_FT_SUB_VOLUME_LIGHT, volume_light_vpt_unival_max, @@ -1460,9 +1879,9 @@ static const shader_sub_table VOLLIT_table[] = { "volume_light", "volume_light", }, -}; +};*/ -static const shader_sub_table FENCE_table[] = { +/*static const shader_sub_table FENCE_table[] = { { SHADER_FT_SUB_FENCE_ALPHA, fence_alpha_vpt_unival_max, @@ -1470,7 +1889,7 @@ static const shader_sub_table FENCE_table[] = { "fence_alpha", "fence_alpha", }, -}; +};*/ static const shader_sub_table RIPPLE_table[] = { { @@ -1512,7 +1931,7 @@ static const shader_sub_table GLITTER_PT_table[] = { }, }; -static const shader_sub_table SHOWVEC_table[] = { +/*static const shader_sub_table SHOW_VEC_table[] = { { SHADER_FT_SUB_SHOW_VECTOR, show_vector_vpt_unival_max, @@ -1520,7 +1939,7 @@ static const shader_sub_table SHOWVEC_table[] = { "show_vector", "show_vector", }, -}; +};*/ static const shader_sub_table FONT_table[] = { { @@ -1542,7 +1961,7 @@ static const shader_sub_table MOVIE_table[] = { }, }; -static const shader_sub_table IMGFILT_table[] = { +/*static const shader_sub_table IMGFILT_table[] = { { SHADER_FT_SUB_IMGFILTER, imgfilter_vpt_unival_max, @@ -1550,6 +1969,30 @@ static const shader_sub_table IMGFILT_table[] = { "imgfilter", "imgfilter", }, +};*/ + +static const shader_sub_table IMGFILT_table[] = { + { + SHADER_FT_SUB_BOX4, + box4_vpt_unival_max, + box4_fpt_unival_max, + "box4", + "box4", + }, + { + SHADER_FT_SUB_BOX8, + box8_vpt_unival_max, + box8_fpt_unival_max, + "box8", + "box8", + }, + { + SHADER_FT_SUB_COPY, + copy_vpt_unival_max, + copy_fpt_unival_max, + "copy", + "copy", + }, }; static const shader_sub_table SPRITE_table[] = { @@ -1562,8 +2005,66 @@ static const shader_sub_table SPRITE_table[] = { }, }; +static const shader_sub_table DOF_table[] = { // Added + { + SHADER_FT_SUB_DOF_RENDER_TILE, + dof_render_tile_vpt_unival_max, + dof_render_tile_fpt_unival_max, + "dof_common", + "dof_render_tile", + }, + { + SHADER_FT_SUB_DOF_GATHER_TILE, + dof_gather_tile_vpt_unival_max, + dof_gather_tile_fpt_unival_max, + "dof_common", + "dof_gather_tile", + }, + { + SHADER_FT_SUB_DOF_DOWNSAMPLE, + dof_downsample_vpt_unival_max, + dof_downsample_fpt_unival_max, + "dof_common", + "dof_downsample", + }, + { + SHADER_FT_SUB_DOF_MAIN_FILTER, + dof_main_filter_vpt_unival_max, + dof_main_filter_fpt_unival_max, + "dof_common", + "dof_main_filter", + }, + { + SHADER_FT_SUB_DOF_UPSAMPLE, + dof_upsample_vpt_unival_max, + dof_upsample_fpt_unival_max, + "dof_common", + "dof_upsample", + }, +}; + +static const shader_sub_table GRID_table[] = { // Added + { + SHADER_FT_SUB_GRID, + grid_vpt_unival_max, + grid_fpt_unival_max, + "grid", + "grid", + }, +}; + +static const shader_sub_table TRANSPARENCY_table[] = { // Added + { + SHADER_FT_SUB_TRANSPARENCY, + transparency_vpt_unival_max, + transparency_fpt_unival_max, + "transparency", + "transparency", + }, +}; + static const uniform_name BLINN_uniform[] = { - U16, + //U16, // 0th removed U_BONE_MAT, U_TEXTURE_COUNT, U_LIGHT_0, @@ -1578,7 +2079,7 @@ static const uniform_name BLINN_uniform[] = { }; static const uniform_name ITEM_uniform[] = { - U16, + //U16, // 0th removed U_BONE_MAT, U_MORPH, U_SPECULAR_IBL, @@ -1595,7 +2096,7 @@ static const uniform_name ITEM_uniform[] = { }; static const uniform_name STAGE_uniform[] = { - U16, + //U16, // 0th removed U_BONE_MAT, U_MORPH, U_MORPH_COLOR, @@ -1606,11 +2107,12 @@ static const uniform_name STAGE_uniform[] = { U_FOG_HEIGHT, U_LIGHT_1, U12, - U_ALPHA_TEST, // 11th added + U_ALPHA_TEST, // 11th added + U_TEXTURE_BLEND, // 12th added }; static const uniform_name SKIN_uniform[] = { - U16, + //U16, // 0th removed U_BONE_MAT, U_NORMAL, U_SELF_SHADOW, @@ -1623,7 +2125,7 @@ static const uniform_name SKIN_uniform[] = { }; static const uniform_name SSS_SKIN_uniform[] = { - U16, + //U16, // 0th removed U_BONE_MAT, U37, U_ALPHA_TEST, @@ -1633,22 +2135,14 @@ static const uniform_name SSS_SKIN_uniform[] = { U26, }; -/*static const uniform_name SSS_FILT_uniform[] = { - U16, +static const uniform_name SSS_FILT_uniform[] = { + //U16, // 0th removed U_SSS_FILTER, U_NPR, -};*/ - -static const uniform_name SSS_FILT_MIN_uniform[] = { - U_INVALID, -}; - -static const uniform_name SSS_FILT_GAUSS_2D_uniform[] = { - U16, }; static const uniform_name HAIR_uniform[] = { - U16, + //U16, // 0th removed U_BONE_MAT, U_SPECULAR_IBL, U_SPECULAR, @@ -1666,7 +2160,7 @@ static const uniform_name HAIR_uniform[] = { }; static const uniform_name CLOTH_uniform[] = { - U16, + //U16, // 0th removed U_BONE_MAT, U_SPECULAR_IBL, U_SPECULAR, @@ -1684,7 +2178,7 @@ static const uniform_name CLOTH_uniform[] = { }; static const uniform_name TIGHTS_uniform[] = { - U16, + //U16, // 0th removed U_BONE_MAT, U_SPECULAR, U_ENV_MAP, @@ -1707,19 +2201,19 @@ static const uniform_name SKY_uniform[] = { U_ALPHA_TEST, // 6th added }; -static const uniform_name EYEBALL_uniform[] = { +/*static const uniform_name EYEBALL_uniform[] = { U_BONE_MAT, U_ENV_MAP, -}; +};*/ -static const uniform_name EYELENS_uniform[] = { +/*static const uniform_name EYELENS_uniform[] = { U_BONE_MAT, U_SPECULAR, U_ENV_MAP, -}; +};*/ static const uniform_name GLASEYE_uniform[] = { - U16, + //U16, // 0th removed U_BONE_MAT, U_SELF_SHADOW, U_FOG, @@ -1729,45 +2223,37 @@ static const uniform_name GLASEYE_uniform[] = { U_TONE_CURVE, }; -static const uniform_name MEMBRAN_uniform[] = { +/*static const uniform_name MEMBRAN_uniform[] = { U_BONE_MAT, U_NORMAL, U_MEMBRANE, -}; +};*/ -static const uniform_name SHDMAP_uniform[] = { +/*static const uniform_name SHDMAP_uniform[] = { U_BONE_MAT, U_TEXTURE_COUNT, U_TRANSPARENCY, U_LIGHT_1, U2D, -}; +};*/ -static const uniform_name ESM_uniform[] = { - U16, +/*static const uniform_name ESM_uniform[] = { + //U16, // 0th removed U_BONE_MAT, U_TEXTURE_COUNT, U_TRANSPARENCY, -}; +};*/ static const uniform_name ESMGAUSS_uniform[] = { U_LIGHT_PROJ, }; -/*static const uniform_name ESMFILT_uniform[] = { +static const uniform_name ESMFILT_uniform[] = { U_ESM_FILTER, -};*/ - -static const uniform_name ESMFILT_MIN_uniform[] = { - U_INVALID, -}; - -static const uniform_name ESMFILT_EROSION_uniform[] = { - U_INVALID, }; static const uniform_name LITPROJ_uniform[] = { - U16, + //U16, // 0th removed U_BONE_MAT, U_SPECULAR, U_NORMAL, @@ -1776,17 +2262,17 @@ static const uniform_name LITPROJ_uniform[] = { }; static const uniform_name SIMPLE_uniform[] = { - U16, + //U16, // 0th removed U_BONE_MAT, U_TEXTURE_COUNT, - U_NORMAL, - U_INSTANCE, - U2E, + //U_NORMAL, // 3rd removed + //U_INSTANCE, // 4th removed + //U2E, // 5th removed U_ALPHA_TEST, // 6th added }; static const uniform_name SIL_uniform[] = { - U16, + //U16, // 0th removed U_BONE_MAT, U_TRANSPARENCY, U_TEXTURE_COUNT, @@ -1797,7 +2283,7 @@ static const uniform_name SIL_uniform[] = { }; static const uniform_name LAMBERT_uniform[] = { - U16, + //U16, // 0th removed U_BONE_MAT, U_TEXTURE_COUNT, U_LIGHT_0, @@ -1809,7 +2295,7 @@ static const uniform_name LAMBERT_uniform[] = { }; static const uniform_name CONSTANT_uniform[] = { - U16, + //U16, // 0th removed U_BONE_MAT, U_TEXTURE_COUNT, U_FOG_HEIGHT, @@ -1818,13 +2304,13 @@ static const uniform_name CONSTANT_uniform[] = { U_ALPHA_TEST, // 6th added }; -static const uniform_name PEEL_uniform[] = { +/*static const uniform_name PEEL_uniform[] = { U_DEPTH_PEEL, U_BONE_MAT, -}; +};*/ static const uniform_name TONEMAP_uniform[] = { - U16, + //U16, // 0th removed U_TONE_MAP, U_FLARE, U_SCENE_FADE, @@ -1843,27 +2329,15 @@ static const uniform_name MAGNIFY_uniform[] = { U_MAGNIFY, }; -/*static const uniform_name MLAA_uniform[] = { +static const uniform_name MLAA_uniform[] = { //U16, // 0th removed U_ALPHA_MASK, U_MLAA, U_MLAA_SEARCH, -};*/ - -static const uniform_name MLAA_EDGE_uniform[] = { - U_ALPHA_MASK, -}; - -static const uniform_name MLAA_AREA_uniform[] = { - U_MLAA_SEARCH, -}; - -static const uniform_name MLAA_BLEND_uniform[] = { - U_ALPHA_MASK, }; static const uniform_name CONTOUR_uniform[] = { - U16, + //U16, // 0th removed U24, }; @@ -1871,42 +2345,26 @@ static const uniform_name CONTOUR_NPR_uniform[] = { U_INVALID, }; -/*static const uniform_name EXPOSURE_uniform[] = { +static const uniform_name EXPOSURE_uniform[] = { U_EXPOSURE, -};*/ - -static const uniform_name EXPOSURE_MINIFY_uniform[] = { - U_INVALID, }; -static const uniform_name EXPOSURE_MEASURE_uniform[] = { - U_INVALID, -}; - -static const uniform_name EXPOSURE_AVERAGE_uniform[] = { - U_INVALID, -}; - -/*static const uniform_name GAUSS_uniform[] = { +static const uniform_name GAUSS_uniform[] = { U_GAUSS, -};*/ - -static const uniform_name GAUSS_USUAL_uniform[] = { - U_INVALID, -}; - -static const uniform_name GAUSS_CONE_uniform[] = { - U_INVALID, }; static const uniform_name SUN_uniform[] = { U_INVALID, }; -static const uniform_name FADE_uniform[] = { - U_FADE, +static const uniform_name SUN_NO_TEXTURED_uniform[] = { + U_INVALID, }; +/*static const uniform_name FADE_uniform[] = { + U_FADE, +};*/ + static const uniform_name WATER01_uniform[] = { U_MORPH, U_MORPH_COLOR, @@ -1919,14 +2377,14 @@ static const uniform_name WATER01_uniform[] = { U_ALPHA_TEST, // 8th added }; -static const uniform_name WATER02_uniform[] = { +/*static const uniform_name WATER02_uniform[] = { U_INVALID, -}; +};*/ -static const uniform_name WATRING_uniform[] = { +/*static const uniform_name WATRING_uniform[] = { U_LIGHT_0, U_LIGHT_1, -}; +};*/ static const uniform_name W_PTCL_uniform[] = { U_ALPHA_TEST, // 0th added @@ -1940,19 +2398,11 @@ static const uniform_name LEAF_PT_uniform[] = { U_INVALID, }; -/*static const uniform_name STAR_uniform[] = { - U_STAR, -};*/ - static const uniform_name STAR_uniform[] = { - U_INVALID, + U_STAR, }; -static const uniform_name STAR_MILKY_WAY_uniform[] = { - U_INVALID, -}; - -static const uniform_name SNORING_uniform[] = { +/*static const uniform_name SNORING_uniform[] = { U_TEXTURE_COUNT, U_NORMAL, U_SPECULAR, @@ -1970,7 +2420,7 @@ static const uniform_name SN_TSL_uniform[] = { static const uniform_name SN_NRM_uniform[] = { U_INVALID, -}; +};*/ static const uniform_name FLOOR_uniform[] = { U_TEXTURE_COUNT, @@ -1991,7 +2441,7 @@ static const uniform_name PUDDLE_uniform[] = { }; static const uniform_name S_REFL_uniform[] = { - U16, + //U16, // 0th removed U_BONE_MAT, U_REFLECT, U_MORPH, @@ -2017,14 +2467,14 @@ static const uniform_name RAIN_uniform[] = { U_INVALID, }; -static const uniform_name VOLLIT_uniform[] = { +/*static const uniform_name VOLLIT_uniform[] = { U_INVALID, -}; +};*/ -static const uniform_name FENCE_uniform[] = { +/*static const uniform_name FENCE_uniform[] = { U_INSTANCE, U_FOG_HEIGHT, -}; +};*/ static const uniform_name RIPPLE_uniform[] = { U_RIPPLE, @@ -2045,14 +2495,15 @@ static const uniform_name GLITTER_PT_uniform[] = { U_ALPHA_BLEND, }; -static const uniform_name SHOWVEC_uniform[] = { - U16, +/*static const uniform_name SHOW_VEC_uniform[] = { + //U16, // 0th removed U_BONE_MAT, U_SHOW_VECTOR, -}; +};*/ static const uniform_name FONT_uniform[] = { - U16, + //U16, // 0th removed + U_INVALID, // 0th removed, 1st added }; static const uniform_name MOVIE_uniform[] = { @@ -2066,10 +2517,31 @@ static const uniform_name IMGFILT_uniform[] = { static const uniform_name SPRITE_uniform[] = { U_TEX_0_TYPE, U_TEX_1_TYPE, - U_SPRITE_BLEND, + U_COMBINER, +}; + +static const uniform_name DOF_uniform[] = { // Added + U_DOF, +}; + +static const uniform_name COPY_uniform[] = { // Added + U_INVALID, +}; + +static const uniform_name GRID_uniform[] = { // Added + U_INVALID, +}; + +static const uniform_name GHOST_uniform[] = { // Added + U_INVALID, +}; + +static const uniform_name TRANSPARENCY_uniform[] = { // Added + U_INVALID, }; static const bool BLINN_permut[] = { + // true, // 0th removed true, false, false, @@ -2077,17 +2549,16 @@ static const bool BLINN_permut[] = { false, false, false, - false, - false, - false, + true, + true, false, false, // 11th added }; static const bool ITEM_permut[] = { + //true, // 0th removed + true, true, - false, - false, false, false, false, @@ -2102,10 +2573,10 @@ static const bool ITEM_permut[] = { }; static const bool STAGE_permut[] = { + //true, // 0th removed + true, + true, true, - false, - false, - false, false, false, false, @@ -2117,6 +2588,7 @@ static const bool STAGE_permut[] = { }; static const bool SKIN_permut[] = { + //true, // 0th removed true, false, false, @@ -2126,10 +2598,10 @@ static const bool SKIN_permut[] = { false, false, false, - false, }; static const bool SSS_SKIN_permut[] = { + //true, // 0th removed true, false, false, @@ -2137,24 +2609,16 @@ static const bool SSS_SKIN_permut[] = { false, false, false, - false, }; -/*static const bool SSS_FILT_permut[] = { +static const bool SSS_FILT_permut[] = { + //true, // 0th removed true, true, - true, -};*/ - -static const bool SSS_FILT_MIN_permut[] = { - false, -}; - -static const bool SSS_FILT_GAUSS_2D_permut[] = { - true, }; static const bool HAIR_permut[] = { + //true, // 0th removed true, false, false, @@ -2168,11 +2632,11 @@ static const bool HAIR_permut[] = { false, false, false, - false, false, // 14th added }; static const bool CLOTH_permut[] = { + //true, // 0th removed true, false, false, @@ -2187,10 +2651,10 @@ static const bool CLOTH_permut[] = { false, false, false, - false, }; static const bool TIGHTS_permut[] = { + //true, // 0th removed true, false, false, @@ -2201,32 +2665,31 @@ static const bool TIGHTS_permut[] = { false, false, false, - false, }; static const bool SKY_permut[] = { false, false, false, - false, - false, + true, + true, false, false, // 6th added }; -static const bool EYEBALL_permut[] = { +/*static const bool EYEBALL_permut[] = { + true, false, - false, -}; +};*/ -static const bool EYELENS_permut[] = { +/*static const bool EYELENS_permut[] = { + true, false, false, - false, -}; +};*/ static const bool GLASEYE_permut[] = { - true, + //true, // 0th removed true, false, false, @@ -2236,101 +2699,93 @@ static const bool GLASEYE_permut[] = { false, }; -static const bool MEMBRAN_permut[] = { - false, - false, - false, -}; - -static const bool SHDMAP_permut[] = { - false, - false, - false, - false, +/*static const bool MEMBRAN_permut[] = { true, -}; + false, + false, +};*/ -static const bool ESM_permut[] = { +/*static const bool SHDMAP_permut[] = { true, false, false, false, -}; + true, +};*/ + +/*static const bool ESM_permut[] = { + //true, // 0th removed + true, + false, + false, +};*/ static const bool ESMGAUSS_permut[] = { true, }; -/*static const bool ESMFILT_permut[] = { +static const bool ESMFILT_permut[] = { true, -};*/ - -static const bool ESMFILT_MIN_permut[] = { - false, -}; - -static const bool ESMFILT_EROSION_permut[] = { - false, }; static const bool LITPROJ_permut[] = { + //true, // 0th removed true, false, false, false, false, - false, }; static const bool SIMPLE_permut[] = { + //true, // 0th removed true, false, - false, - false, - false, - false, + //false, // 3rd removed + //false, // 4th removed + //false, // 5th removed false, // 6th added }; static const bool SIL_permut[] = { + //true, // 0th removed true, false, false, - false, - false, + true, false, false, false, // 7th added }; static const bool LAMBERT_permut[] = { + //true, // 0th removed true, false, false, false, false, - false, - false, - false, + true, + true, false, // 8th added }; static const bool CONSTANT_permut[] = { + //true, // 0th removed true, false, - false, - false, - false, + true, + true, false, }; -static const bool PEEL_permut[] = { +/*static const bool PEEL_permut[] = { false, - false, -}; + true, +};*/ static const bool TONEMAP_permut[] = { - true, + //true, // 0th removed false, false, false, @@ -2349,27 +2804,15 @@ static const bool MAGNIFY_permut[] = { true, }; -/*static const bool MLAA_permut[] = { +static const bool MLAA_permut[] = { //true, // 0th removed true, true, true, -};*/ - -static const bool MLAA_EDGE_permut[] = { - true, -}; - -static const bool MLAA_AREA_permut[] = { - true, -}; - -static const bool MLAA_BLEND_permut[] = { - true, }; static const bool CONTOUR_permut[] = { - true, + //true, // 0th removed false, }; @@ -2377,45 +2820,29 @@ static const bool CONTOUR_NPR_permut[] = { false, }; -/*static const bool EXPOSURE_permut[] = { +static const bool EXPOSURE_permut[] = { true, -};*/ - -static const bool EXPOSURE_MINIFY_permut[] = { - false, }; -static const bool EXPOSURE_MEASURE_permut[] = { - false, -}; - -static const bool EXPOSURE_AVERAGE_permut[] = { - false, -}; - -/*static const bool GAUSS_permut[] = { +static const bool GAUSS_permut[] = { true, -};*/ - -static const bool GAUSS_USUAL_permut[] = { - false, -}; - -static const bool GAUSS_CONE_permut[] = { - false, }; static const bool SUN_permut[] = { false, }; -static const bool FADE_permut[] = { +static const bool SUN_NO_TEXTURED_permut[] = { false, }; +/*static const bool FADE_permut[] = { + false, +};*/ + static const bool WATER01_permut[] = { - false, - false, + true, + true, false, false, false, @@ -2429,36 +2856,28 @@ static const bool WATER02_permut[] = { false, }; -static const bool WATRING_permut[] = { +/*static const bool WATRING_permut[] = { false, false, -}; +};*/ static const bool W_PTCL_permut[] = { false, // 0th added }; static const bool SNOW_PT_permut[] = { - true, + false, }; static const bool LEAF_PT_permut[] = { false, }; -/*static const bool STAR_permut[] = { - false, -};*/ - static const bool STAR_permut[] = { false, }; -static const bool STAR_MILKY_WAY_permut[] = { - false, -}; - -static const bool SNORING_permut[] = { +/*static const bool SNORING_permut[] = { false, false, false, @@ -2476,7 +2895,7 @@ static const bool SN_TSL_permut[] = { static const bool SN_NRM_permut[] = { false, -}; +};*/ static const bool FLOOR_permut[] = { false, @@ -2484,8 +2903,8 @@ static const bool FLOOR_permut[] = { false, false, false, - false, - false, + true, + true, false, // 7th added }; @@ -2497,11 +2916,11 @@ static const bool PUDDLE_permut[] = { }; static const bool S_REFL_permut[] = { + //true, // 0th removed true, false, - false, - false, - false, + true, + true, false, false, false, @@ -2510,30 +2929,30 @@ static const bool S_REFL_permut[] = { }; static const bool S_REFR_permut[] = { - false, + true, false, }; static const bool RIPEMIT_permut[] = { - false, - false, + true, + true, }; static const bool RAIN_permut[] = { false, }; -static const bool VOLLIT_permut[] = { +/*static const bool VOLLIT_permut[] = { false, -}; +};*/ -static const bool FENCE_permut[] = { +/*static const bool FENCE_permut[] = { false, false, -}; +};*/ static const bool RIPPLE_permut[] = { - false, + true, }; static const bool FOGPTCL_permut[] = { @@ -2551,14 +2970,15 @@ static const bool GLITTER_PT_permut[] = { false, }; -static const bool SHOWVEC_permut[] = { - true, +/*static const bool SHOW_VEC_permut[] = { + //true, // 0th removed false, false, -}; +};*/ static const bool FONT_permut[] = { - true, + //true, // 0th removed + false, // 0th removed, 1st added }; static const bool MOVIE_permut[] = { @@ -2566,7 +2986,7 @@ static const bool MOVIE_permut[] = { }; static const bool IMGFILT_permut[] = { - true, + false, }; static const bool SPRITE_permut[] = { @@ -2575,6 +2995,26 @@ static const bool SPRITE_permut[] = { false, }; +static const bool DOF_permut[] = { // Added + true, +}; + +static const bool COPY_permut[] = { // Added + false, +}; + +static const bool GRID_permut[] = { // Added + false, +}; + +static const bool GHOST_permut[] = { // Added + false, +}; + +static const bool TRANSPARENCY_permut[] = { // Added + false, +}; + struct glass_eye_struct { vec4 field_0; vec4 field_10; @@ -2630,130 +3070,255 @@ const shader_table shader_ft_table[] = { shader_table_struct(STAGE), shader_table_struct(SKIN), shader_table_struct(SSS_SKIN), - //shader_table_struct(SSS_FILT), - shader_table_struct(SSS_FILT_MIN), - shader_table_struct(SSS_FILT_GAUSS_2D), + shader_table_struct(SSS_FILT), shader_table_struct(HAIR), shader_table_struct(CLOTH), shader_table_struct(TIGHTS), shader_table_struct(SKY), - shader_table_struct(EYEBALL), - shader_table_struct(EYELENS), + //shader_table_struct(EYEBALL), + //shader_table_struct(EYELENS), shader_table_struct(GLASEYE), - shader_table_struct(MEMBRAN), - shader_table_struct(SHDMAP), - shader_table_struct(ESM), + //shader_table_struct(MEMBRAN), + //shader_table_struct(SHDMAP), + //shader_table_struct(ESM), shader_table_struct(ESMGAUSS), - //shader_table_struct(ESMFILT), - shader_table_struct(ESMFILT_MIN), - shader_table_struct(ESMFILT_EROSION), + shader_table_struct(ESMFILT), shader_table_struct(LITPROJ), shader_table_struct(SIMPLE), shader_table_struct(SIL), shader_table_struct(LAMBERT), shader_table_struct(CONSTANT), - shader_table_struct(PEEL), + //shader_table_struct(PEEL), shader_table_struct(TONEMAP), shader_table_struct(REDUCE), shader_table_struct(MAGNIFY), - //shader_table_struct(MLAA), - shader_table_struct(MLAA_EDGE), - shader_table_struct(MLAA_AREA), - shader_table_struct(MLAA_BLEND), + shader_table_struct(MLAA), shader_table_struct(CONTOUR), shader_table_struct(CONTOUR_NPR), - //shader_table_struct(EXPOSURE), - shader_table_struct(EXPOSURE_MINIFY), - shader_table_struct(EXPOSURE_MEASURE), - shader_table_struct(EXPOSURE_AVERAGE), - //shader_table_struct(GAUSS), - shader_table_struct(GAUSS_USUAL), - shader_table_struct(GAUSS_CONE), + shader_table_struct(EXPOSURE), + shader_table_struct(GAUSS), shader_table_struct(SUN), - shader_table_struct(FADE), + shader_table_struct(SUN_NO_TEXTURED), + //shader_table_struct(FADE), shader_table_struct(WATER01), - shader_table_struct(WATER02), - shader_table_struct(WATRING), + //shader_table_struct(WATER02), + //shader_table_struct(WATRING), shader_table_struct(W_PTCL), shader_table_struct(SNOW_PT), shader_table_struct(LEAF_PT), shader_table_struct(STAR), - shader_table_struct(STAR_MILKY_WAY), - shader_table_struct(SNORING), + /*shader_table_struct(SNORING), shader_table_struct(SN_FOOT), shader_table_struct(SN_TSL), - shader_table_struct(SN_NRM), + shader_table_struct(SN_NRM),*/ shader_table_struct(FLOOR), shader_table_struct(PUDDLE), shader_table_struct(S_REFL), shader_table_struct(S_REFR), shader_table_struct(RIPEMIT), shader_table_struct(RAIN), - shader_table_struct(VOLLIT), - shader_table_struct(FENCE), + //shader_table_struct(VOLLIT), + //shader_table_struct(FENCE), shader_table_struct(RIPPLE), shader_table_struct(FOGPTCL), shader_table_struct(PARTICL), shader_table_struct(GLITTER_PT), - shader_table_struct(SHOWVEC), + //shader_table_struct(SHOW_VEC), shader_table_struct(FONT), shader_table_struct(MOVIE), shader_table_struct(IMGFILT), shader_table_struct(SPRITE), + shader_table_struct(DOF), // Added + shader_table_struct(GRID), // Added + shader_table_struct(TRANSPARENCY), // Added }; +#undef shader_table_struct #undef shader_table_struct const size_t shader_ft_table_size = sizeof(shader_ft_table) / sizeof(shader_table); static void glass_eye_calc(glass_eye_struct* glass_eye); -static void glass_eye_set(glass_eye_struct* glass_eye, shader_set_data* set); -static void shader_bind_blinn(shader_set_data* set, shader* shad); -static void shader_bind_cloth(shader_set_data* set, shader* shad); -static void shader_bind_hair(shader_set_data* set, shader* shad); -static void shader_bind_membrane(shader_set_data* set, shader* shad); -static void shader_bind_eye_ball(shader_set_data* set, shader* shad); -static void shader_bind_tone_map(shader_set_data* set, shader* shad); -static void shader_bind_sss_filter_min(shader_set_data* set, shader* shad); +static void glass_eye_set(glass_eye_struct* glass_eye); +static void shader_opengl_bind_blinn(shader_set_data* set, shader_opengl* shad); +static void shader_opengl_bind_cloth(shader_set_data* set, shader_opengl* shad); +static void shader_opengl_bind_hair(shader_set_data* set, shader_opengl* shad); +static void shader_opengl_bind_eye_ball(shader_set_data* set, shader_opengl* shad); +static void shader_opengl_bind_tone_map(shader_set_data* set, shader_opengl* shad); +static void shader_opengl_bind_sss_filter(shader_set_data* set, shader_opengl* shad); // Added +static void shader_opengl_bind_esm_filter(shader_set_data* set, shader_opengl* shad); // Added +static void shader_opengl_bind_reduce_tex(shader_set_data* set, shader_opengl* shad); // Added +static void shader_opengl_bind_magnify(shader_set_data* set, shader_opengl* shad); // Added +static void shader_opengl_bind_mlaa(shader_set_data* set, shader_opengl* shad); // Added +static void shader_opengl_bind_exposure(shader_set_data* set, shader_opengl* shad); // Added +static void shader_opengl_bind_gauss(shader_set_data* set, shader_opengl* shad); // Added +static void shader_opengl_bind_snow_particle(shader_set_data* set, shader_opengl* shad); // Added +static void shader_opengl_bind_star(shader_set_data* set, shader_opengl* shad); // Added +static void shader_opengl_bind_imgfilter(shader_set_data* set, shader_opengl* shad); // Added +static void shader_opengl_bind_dof(shader_set_data* set, shader_opengl* shad); // Added +static shader_vulkan_pair shader_vulkan_get_blinn(shader_set_data* set, shader_vulkan* shad); +static shader_vulkan_pair shader_vulkan_get_cloth(shader_set_data* set, shader_vulkan* shad); +static shader_vulkan_pair shader_vulkan_get_hair(shader_set_data* set, shader_vulkan* shad); +static shader_vulkan_pair shader_vulkan_get_eye_ball(shader_set_data* set, shader_vulkan* shad); +static shader_vulkan_pair shader_vulkan_get_tone_map(shader_set_data* set, shader_vulkan* shad); +static shader_vulkan_pair shader_vulkan_get_sss_filter(shader_set_data* set, shader_vulkan* shad); // Added +static shader_vulkan_pair shader_vulkan_get_esm_filter(shader_set_data* set, shader_vulkan* shad); // Added +static shader_vulkan_pair shader_vulkan_get_reduce_tex(shader_set_data* set, shader_vulkan* shad); // Added +static shader_vulkan_pair shader_vulkan_get_magnify(shader_set_data* set, shader_vulkan* shad); // Added +static shader_vulkan_pair shader_vulkan_get_mlaa(shader_set_data* set, shader_vulkan* shad); // Added +static shader_vulkan_pair shader_vulkan_get_exposure(shader_set_data* set, shader_vulkan* shad); // Added +static shader_vulkan_pair shader_vulkan_get_gauss(shader_set_data* set, shader_vulkan* shad); // Added +static shader_vulkan_pair shader_vulkan_get_snow_particle(shader_set_data* set, shader_vulkan* shad); // Added +static shader_vulkan_pair shader_vulkan_get_star(shader_set_data* set, shader_vulkan* shad); // Added +static shader_vulkan_pair shader_vulkan_get_imgfilter(shader_set_data* set, shader_vulkan* shad); // Added +static shader_vulkan_pair shader_vulkan_get_dof(shader_set_data* set, shader_vulkan* shad); // Added -const shader_bind_func shader_ft_bind_func_table[] = { +const shader_opengl_bind_func shader_ft_opengl_bind_func_table[] = { { SHADER_FT_BLINN, - shader_bind_blinn, + shader_opengl_bind_blinn, }, { SHADER_FT_CLOTH, - shader_bind_cloth, + shader_opengl_bind_cloth, }, { SHADER_FT_HAIR, - shader_bind_hair, + shader_opengl_bind_hair, }, { - SHADER_FT_MEMBRAN, - shader_bind_membrane, - }, - { - SHADER_FT_EYEBALL, - shader_bind_eye_ball, + //SHADER_FT_EYEBALL, + SHADER_FT_GLASEYE, + shader_opengl_bind_eye_ball, }, { SHADER_FT_TONEMAP, - shader_bind_tone_map, + shader_opengl_bind_tone_map, }, - { - SHADER_FT_SSS_FILT_MIN, - shader_bind_sss_filter_min, + { // Added + SHADER_FT_SSS_FILT, + shader_opengl_bind_sss_filter, + }, + { // Added + SHADER_FT_ESMFILT, + shader_opengl_bind_esm_filter, + }, + { // Added + SHADER_FT_REDUCE, + shader_opengl_bind_reduce_tex, + }, + { // Added + SHADER_FT_MAGNIFY, + shader_opengl_bind_magnify, + }, + { // Added + SHADER_FT_MLAA, + shader_opengl_bind_mlaa, + }, + { // Added + SHADER_FT_EXPOSURE, + shader_opengl_bind_exposure, + }, + { // Added + SHADER_FT_GAUSS, + shader_opengl_bind_gauss, + }, + { // Added + SHADER_FT_SNOW_PT, + shader_opengl_bind_snow_particle, + }, + { // Added + SHADER_FT_STAR, + shader_opengl_bind_star, + }, + { // Added + SHADER_FT_IMGFILT, + shader_opengl_bind_imgfilter, + }, + { // Added + SHADER_FT_DOF, + shader_opengl_bind_dof, }, }; -const size_t shader_ft_bind_func_table_size = - sizeof(shader_ft_bind_func_table) / sizeof(shader_bind_func); +const size_t shader_ft_opengl_bind_func_table_size = + sizeof(shader_ft_opengl_bind_func_table) / sizeof(shader_opengl_bind_func); + +const shader_vulkan_get_func shader_ft_vulkan_get_func_table[] = { + { + SHADER_FT_BLINN, + shader_vulkan_get_blinn, + }, + { + SHADER_FT_CLOTH, + shader_vulkan_get_cloth, + }, + { + SHADER_FT_HAIR, + shader_vulkan_get_hair, + }, + { + //SHADER_FT_EYEBALL, + SHADER_FT_GLASEYE, + shader_vulkan_get_eye_ball, + }, + { + SHADER_FT_TONEMAP, + shader_vulkan_get_tone_map, + }, + { // Added + SHADER_FT_SSS_FILT, + shader_vulkan_get_sss_filter, + }, + { // Added + SHADER_FT_ESMFILT, + shader_vulkan_get_esm_filter, + }, + { // Added + SHADER_FT_REDUCE, + shader_vulkan_get_reduce_tex, + }, + { // Added + SHADER_FT_MAGNIFY, + shader_vulkan_get_magnify, + }, + { // Added + SHADER_FT_MLAA, + shader_vulkan_get_mlaa, + }, + { // Added + SHADER_FT_EXPOSURE, + shader_vulkan_get_exposure, + }, + { // Added + SHADER_FT_GAUSS, + shader_vulkan_get_gauss, + }, + { // Added + SHADER_FT_SNOW_PT, + shader_vulkan_get_snow_particle, + }, + { // Added + SHADER_FT_STAR, + shader_vulkan_get_star, + }, + { // Added + SHADER_FT_IMGFILT, + shader_vulkan_get_imgfilter, + }, + { // Added + SHADER_FT_DOF, + shader_vulkan_get_dof, + }, +}; + +const size_t shader_ft_vulkan_get_func_table_size = + sizeof(shader_ft_vulkan_get_func_table) / sizeof(shader_vulkan_get_func); shader_set_data shaders_ft; -glass_eye_struct glass_eye = { +static glass_eye_struct glass_eye = { { 5.0f, 5.0f, 0.5f, 0.5f }, { 2.5f, 2.5f, 0.5f, 0.5f }, 1.0f, @@ -2782,6 +3347,12 @@ glass_eye_struct glass_eye = { 0, }; +int32_t shader_ft_get_index_by_name(const char* name) { + if (!str_utils_compare(name, "EYEBALL")) + return SHADER_FT_GLASEYE; + return -1; +} + static void glass_eye_calc(glass_eye_struct* glass_eye) { float_t v2 = glass_eye->field_28; glass_eye->field_64 = glass_eye->field_2C / v2; @@ -2801,10 +3372,8 @@ static void glass_eye_calc(glass_eye_struct* glass_eye) { glass_eye->frame = frame; if (frame == (frame / 90) * 90) { - glass_eye->field_A0.x = ((float_t)(90.0 / (double_t)(frame % 90)) - 0.5f) * 0.015f; - glass_eye->field_A0.y = ((float_t)((double_t)(frame % 90) / 90.0) - 0.5f) * 0.015f; - //glass_eye->field_A0.x = (mt_random_get_float_value_by_rand_state_id(0) - 0.5f) * 0.015f; - //glass_eye->field_A0.y = (mt_random_get_float_value_by_rand_state_id(0) - 0.5f) * 0.015f; + glass_eye->field_A0.x = (rand_state_array_get_float(0) - 0.5f) * 0.015f; + glass_eye->field_A0.y = (rand_state_array_get_float(0) - 0.5f) * 0.015f; } float_t v17 = (float_t)(uint8_t)frame * (float_t)(M_PI * (1.0 / 128.0)); @@ -2814,115 +3383,482 @@ static void glass_eye_calc(glass_eye_struct* glass_eye) { glass_eye->field_A0.z = (v19 + v20) * 0.5f * 0.01f; } -static void glass_eye_set(glass_eye_struct* glass_eye, shader_set_data* set) { +static void glass_eye_set(glass_eye_struct* glass_eye) { + glass_eye_batch_shader_data glass_eye_batch = {}; + vec4 temp; *(vec3*)&temp = glass_eye->field_68 * glass_eye->field_68; temp.w = temp.z; *(vec3*)&temp = vec3::rcp(*(vec3*)&temp); - set->local_vert_set(0x0A, temp); - set->local_frag_set(0x0A, temp); + glass_eye_batch.g_ellipsoid_radius = temp; *(vec3*)&temp = glass_eye->field_68; temp.w = 1.0f; - set->local_vert_set(0x0B, temp); - set->local_frag_set(0x0B, temp); + glass_eye_batch.g_ellipsoid_scale = temp; - temp = glass_eye->field_0; - set->local_vert_set(0x0C, temp); - temp = glass_eye->field_A0; - set->local_vert_set(0x0D, temp); + glass_eye_batch.g_tex_model_param = glass_eye->field_0; + glass_eye_batch.g_tex_offset = glass_eye->field_A0; *(vec3*)&temp = glass_eye->field_90 * glass_eye->field_90; temp.w = temp.z; *(vec3*)&temp = vec3::rcp(*(vec3*)&temp); - set->local_vert_set(0x0E, temp); + glass_eye_batch.g_eb_radius = temp; - temp = glass_eye->field_10; - set->local_vert_set(0x0F, temp); - - *(vec3*)&temp = glass_eye->field_68 * glass_eye->field_68; - temp.w = temp.z; - *(vec3*)&temp = vec3::rcp(*(vec3*)&temp); - set->local_vert_set(0x0E, temp); + glass_eye_batch.g_eb_tex_model_param = glass_eye->field_10; float_t v2 = (glass_eye->field_20 - glass_eye->field_24) / (glass_eye->field_20 + glass_eye->field_24); v2 *= v2; - set->local_frag_set(0x0C, 1.0f - v2, v2, 0.0f, 0.0f); + glass_eye_batch.g_fresnel = { 1.0f - v2, v2, 0.0f, 0.0f }; float_t v3 = (glass_eye->field_20 * glass_eye->field_20) / (glass_eye->field_24 * glass_eye->field_24); - set->local_frag_set(0x0D, v3, 1.0f - v3, glass_eye->field_20 / glass_eye->field_24, 0.0f); + glass_eye_batch.g_refract1 = { v3, 1.0f - v3, glass_eye->field_20 / glass_eye->field_24, 0.0f }; float_t v4 = (glass_eye->field_24 * glass_eye->field_24) / (glass_eye->field_20 * glass_eye->field_20); - set->local_frag_set(0x0E, v4, 1.0f - v4, glass_eye->field_24 / glass_eye->field_20, 0.0f); + glass_eye_batch.g_refract2 = { v4, 1.0f - v4, glass_eye->field_24 / glass_eye->field_20, 0.0f }; *(vec3*)&temp = glass_eye->field_74 * glass_eye->field_74; temp.w = -1.0f; *(vec3*)&temp = vec3::rcp(*(vec3*)&temp); - set->local_frag_set(0x0F, temp); + glass_eye_batch.g_iris_radius = temp; *(vec3*)&temp = glass_eye->field_68 * glass_eye->field_68; temp.w = -1.0f; *(vec3*)&temp = vec3::rcp(*(vec3*)&temp); - set->local_frag_set(0x10, temp); + glass_eye_batch.g_cornea_radius = temp; *(vec3*)&temp = glass_eye->field_80 * glass_eye->field_80; temp.w = -1.0f; *(vec3*)&temp = vec3::rcp(*(vec3*)&temp); - set->local_frag_set(0x11, temp); + glass_eye_batch.g_pupil_radius = temp; *(vec2*)&temp = *(vec2*)&glass_eye->field_0 * *(vec2*)&glass_eye->field_74; temp.z = glass_eye->field_64 * 1.442695f; temp.w = glass_eye->field_8C; *(vec2*)&temp = vec2::rcp(*(vec2*)&temp); - set->local_frag_set(0x12, temp); + glass_eye_batch.g_tex_scale = temp; + + extern render_context* rctx_ptr; + rctx_ptr->glass_eye_batch_ubo.WriteMapMemory(glass_eye_batch); + rctx_ptr->glass_eye_batch_ubo.Bind(4); } -static void shader_bind_blinn(shader_set_data* set, shader* shad) { +static void shader_opengl_bind_blinn(shader_set_data* set, shader_opengl* shad) { shad->bind(set, uniform_value[U_NORMAL] ? SHADER_FT_SUB_BLINN_FRAG : SHADER_FT_SUB_BLINN_VERT); } -static void shader_bind_cloth(shader_set_data* set, shader* shad) { +static void shader_opengl_bind_cloth(shader_set_data* set, shader_opengl* shad) { shad->bind(set, uniform_value[U_NPR] ? SHADER_FT_SUB_CLOTH_NPR1 : (uniform_value[U_ANISO] ? SHADER_FT_SUB_CLOTH_ANISO : SHADER_FT_SUB_CLOTH_DEFAULT)); } -static void shader_bind_hair(shader_set_data* set, shader* shad) { - shad->bind(set, uniform_value[U_NPR] ? SHADER_FT_SUB_HAIR_NPR1 - : (uniform_value[U_ANISO] ? SHADER_FT_SUB_HAIR_ANISO : SHADER_FT_SUB_HAIR_DEFAULT)); +static void shader_opengl_bind_hair(shader_set_data* set, shader_opengl* shad) { + shad->bind(set, uniform_value[U_NPR] ? SHADER_FT_SUB_HAIR_NPR1 : SHADER_FT_SUB_HAIR_DEFAULT); } -static void shader_bind_membrane(shader_set_data* set, shader* shad) { - uniform_value[U_MEMBRANE] = 3; - if (shad->bind(set, SHADER_FT_SUB_MEMBRANE) < 0) - return; - - /*uint32_t(* sub_140192E00)() = (void*)0x0000000140192E00; - uint32_t v1 = sub_140192E00(); - mat4 mat = mat4_identity; - mat4_rotate_x_mult(&mat, (float_t)((v1 & 0x1FF) * (M_PI / 256.0)), &mat); - mat4_rotate_z_mult(&mat, (float_t)((v1 % 0x168) * (M_PI / 180.0)), &mat);*/ - - //vec4 vec = (vec4){ 1.0f, 0.0f, 0.0f, 0.0f }; - //mat4_mult_vec(&mat, &vec, &vec); - - //set->local_frag_set(10, vec.x, vec.y, vec.z, 0.0f); -} - -static void shader_bind_eye_ball(shader_set_data* set, shader* shad) { +static void shader_opengl_bind_eye_ball(shader_set_data* set, shader_opengl* shad) { uniform_value[U18] = 0; - if (set->shaders[SHADER_FT_GLASEYE].bind(set, SHADER_FT_SUB_GLASS_EYE) >= 0) { + if (set->opengl_data->shaders[SHADER_FT_GLASEYE].bind(set, SHADER_FT_SUB_GLASS_EYE) >= 0) { glass_eye_calc(&glass_eye); - glass_eye_set(&glass_eye, set); + glass_eye_set(&glass_eye); } } -static void shader_bind_tone_map(shader_set_data* set, shader* shad) { +static void shader_opengl_bind_tone_map(shader_set_data* set, shader_opengl* shad) { shad->bind(set, uniform_value[U_NPR] == 1 ? SHADER_FT_SUB_TONEMAP_NPR1 : SHADER_FT_SUB_TONEMAP); } -static void shader_bind_sss_filter_min(shader_set_data* set, shader* shad) { - shad->bind(set, uniform_value[U_NPR] - ? SHADER_FT_SUB_SSS_FILTER_MIN_NPR : SHADER_FT_SUB_SSS_FILTER_MIN); +static void shader_opengl_bind_sss_filter(shader_set_data* set, shader_opengl* shad) { // Added + switch (uniform_value[U_SSS_FILTER]) { + case 0: + shad->bind(set, uniform_value[U_NPR] + ? SHADER_FT_SUB_SSS_FILTER_MIN_NPR : SHADER_FT_SUB_SSS_FILTER_MIN); + break; + case 3: + shad->bind(set, SHADER_FT_SUB_SSS_FILTER_GAUSS_2D); + break; + } +} + +static void shader_opengl_bind_esm_filter(shader_set_data* set, shader_opengl* shad) { // Added + switch (uniform_value[U_ESM_FILTER]) { + case 0: + shad->bind(set, SHADER_FT_SUB_ESM_FILTER_MIN); + break; + case 1: + shad->bind(set, SHADER_FT_SUB_ESM_FILTER_EROSION); + break; + } +} + +static void shader_opengl_bind_reduce_tex(shader_set_data* set, shader_opengl* shad) { // Added + switch (uniform_value[U_REDUCE]) { + case 0: + switch (uniform_value[U_ALPHA_MASK]) { + case 0: + shad->bind(set, SHADER_FT_SUB_REDUCE_TEX_REDUCE_2); + break; + default: + shad->bind(set, SHADER_FT_SUB_REDUCE_TEX_REDUCE_2_ALPHAMASK); + break; + } + break; + case 1: + switch (uniform_value[U_ALPHA_MASK]) { + case 0: + shad->bind(set, SHADER_FT_SUB_REDUCE_TEX_REDUCE_4); + break; + } + break; + case 3: + switch (uniform_value[U_ALPHA_MASK]) { + case 0: + shad->bind(set, SHADER_FT_SUB_REDUCE_TEX_REDUCE_4_EXTRACT); + break; + } + break; + case 4: + switch (uniform_value[U_ALPHA_MASK]) { + case 0: + shad->bind(set, SHADER_FT_SUB_GHOST); + break; + } + break; + case 5: + switch (uniform_value[U_ALPHA_MASK]) { + case 0: + shad->bind(set, SHADER_FT_SUB_REDUCE_TEX_REDUCE_COMPOSITE_2); + break; + } + break; + case 6: + switch (uniform_value[U_ALPHA_MASK]) { + case 0: + shad->bind(set, SHADER_FT_SUB_REDUCE_TEX_REDUCE_COMPOSITE_BLUR); + break; + } + break; + case 7: + switch (uniform_value[U_ALPHA_MASK]) { + case 0: + shad->bind(set, SHADER_FT_SUB_REDUCE_TEX_REDUCE_COMPOSITE_4); + break; + } + break; + } +} + +static void shader_opengl_bind_magnify(shader_set_data* set, shader_opengl* shad) { // Added + switch (uniform_value[U_MAGNIFY]) { + case 0: + shad->bind(set, SHADER_FT_SUB_MAGNIFY_LINEAR); + break; + case 2: + shad->bind(set, SHADER_FT_SUB_MAGNIFY_DIFF); + break; + case 3: + shad->bind(set, SHADER_FT_SUB_MAGNIFY_DIFF2); + break; + case 4: + shad->bind(set, SHADER_FT_SUB_MAGNIFY_CONE); + break; + case 5: + shad->bind(set, SHADER_FT_SUB_MAGNIFY_CONE2); + break; + } +} + +static void shader_opengl_bind_mlaa(shader_set_data* set, shader_opengl* shad) { // Added + switch (uniform_value[U_MLAA]) { + case 0: + shad->bind(set, SHADER_FT_SUB_MLAA_EDGE); + break; + case 1: + shad->bind(set, SHADER_FT_SUB_MLAA_AREA); + break; + case 2: + shad->bind(set, SHADER_FT_SUB_MLAA_BLEND); + break; + } +} + +static void shader_opengl_bind_exposure(shader_set_data* set, shader_opengl* shad) { // Added + switch (uniform_value[U_EXPOSURE]) { + case 0: + shad->bind(set, SHADER_FT_SUB_EXPOSURE_MINIFY); + break; + case 1: + shad->bind(set, SHADER_FT_SUB_EXPOSURE_MEASURE); + break; + case 2: + shad->bind(set, SHADER_FT_SUB_EXPOSURE_AVERAGE); + break; + } +} + +static void shader_opengl_bind_gauss(shader_set_data* set, shader_opengl* shad) { // Added + switch (uniform_value[U_GAUSS]) { + case 0: + shad->bind(set, SHADER_FT_SUB_PP_GAUSS_USUAL); + break; + case 1: + shad->bind(set, SHADER_FT_SUB_PP_GAUSS_CONE); + break; + } +} + +static void shader_opengl_bind_snow_particle(shader_set_data* set, shader_opengl* shad) { // Added + switch (uniform_value[U_SNOW_PARTICLE]) { + case 0: + shad->bind(set, SHADER_FT_SUB_SNOW_PARTICLE_CPU); + break; + case 1: + shad->bind(set, SHADER_FT_SUB_SNOW_PARTICLE); + break; + } +} + +static void shader_opengl_bind_star(shader_set_data* set, shader_opengl* shad) { // Added + switch (uniform_value[U_STAR]) { + case 0: + shad->bind(set, SHADER_FT_SUB_STAR); + break; + case 1: + shad->bind(set, SHADER_FT_SUB_STAR_MILKY_WAY); + break; + } +} + +static void shader_opengl_bind_imgfilter(shader_set_data* set, shader_opengl* shad) { + switch (uniform_value[U_IMAGE_FILTER]) { + case 0: + shad->bind(set, SHADER_FT_SUB_BOX4); + break; + case 1: + shad->bind(set, SHADER_FT_SUB_BOX8); + break; + case 5: + shad->bind(set, SHADER_FT_SUB_COPY); + break; + } +} + +static void shader_opengl_bind_dof(shader_set_data* set, shader_opengl* shad) { // Added + switch (uniform_value[U_DOF_STAGE]) { + case 0: + shad->bind(set, SHADER_FT_SUB_DOF_RENDER_TILE); + break; + case 1: + shad->bind(set, SHADER_FT_SUB_DOF_GATHER_TILE); + break; + case 2: + shad->bind(set, SHADER_FT_SUB_DOF_DOWNSAMPLE); + break; + case 3: + shad->bind(set, SHADER_FT_SUB_DOF_MAIN_FILTER); + break; + case 4: + shad->bind(set, SHADER_FT_SUB_DOF_UPSAMPLE); + break; + } +} + +static shader_vulkan_pair shader_vulkan_get_blinn(shader_set_data* set, shader_vulkan* shad) { + return shad->get(set, uniform_value[U_NORMAL] + ? SHADER_FT_SUB_BLINN_FRAG : SHADER_FT_SUB_BLINN_VERT); +} + +static shader_vulkan_pair shader_vulkan_get_cloth(shader_set_data* set, shader_vulkan* shad) { + return shad->get(set, uniform_value[U_NPR] ? SHADER_FT_SUB_CLOTH_NPR1 + : (uniform_value[U_ANISO] ? SHADER_FT_SUB_CLOTH_ANISO : SHADER_FT_SUB_CLOTH_DEFAULT)); +} + +static shader_vulkan_pair shader_vulkan_get_hair(shader_set_data* set, shader_vulkan* shad) { + return shad->get(set, uniform_value[U_NPR] ? SHADER_FT_SUB_HAIR_NPR1 : SHADER_FT_SUB_HAIR_DEFAULT); +} + +static shader_vulkan_pair shader_vulkan_get_eye_ball(shader_set_data* set, shader_vulkan* shad) { + uniform_value[U18] = 0; + shader_vulkan_pair shad_pair = set->vulkan_data->shaders[SHADER_FT_GLASEYE].get(set, SHADER_FT_SUB_GLASS_EYE); + if (!shad_pair.first) + return {}; + + glass_eye_calc(&glass_eye); + glass_eye_set(&glass_eye); + return shad_pair; +} + +static shader_vulkan_pair shader_vulkan_get_tone_map(shader_set_data* set, shader_vulkan* shad) { + return shad->get(set, uniform_value[U_NPR] == 1 + ? SHADER_FT_SUB_TONEMAP_NPR1 : SHADER_FT_SUB_TONEMAP); +} + +static shader_vulkan_pair shader_vulkan_get_sss_filter(shader_set_data* set, shader_vulkan* shad) { // Added + switch (uniform_value[U_SSS_FILTER]) { + case 0: + return shad->get(set, uniform_value[U_NPR] + ? SHADER_FT_SUB_SSS_FILTER_MIN_NPR : SHADER_FT_SUB_SSS_FILTER_MIN); + case 3: + return shad->get(set, SHADER_FT_SUB_SSS_FILTER_GAUSS_2D); + } + return {}; +} + +static shader_vulkan_pair shader_vulkan_get_esm_filter(shader_set_data* set, shader_vulkan* shad) { // Added + switch (uniform_value[U_ESM_FILTER]) { + case 0: + return shad->get(set, SHADER_FT_SUB_ESM_FILTER_MIN); + case 1: + return shad->get(set, SHADER_FT_SUB_ESM_FILTER_EROSION); + } + return {}; +} + +static shader_vulkan_pair shader_vulkan_get_reduce_tex(shader_set_data* set, shader_vulkan* shad) { // Added + switch (uniform_value[U_REDUCE]) { + case 0: + switch (uniform_value[U_ALPHA_MASK]) { + case 0: + return shad->get(set, SHADER_FT_SUB_REDUCE_TEX_REDUCE_2); + default: + return shad->get(set, SHADER_FT_SUB_REDUCE_TEX_REDUCE_2_ALPHAMASK); + } + break; + case 1: + switch (uniform_value[U_ALPHA_MASK]) { + case 0: + return shad->get(set, SHADER_FT_SUB_REDUCE_TEX_REDUCE_4); + } + break; + case 3: + switch (uniform_value[U_ALPHA_MASK]) { + case 0: + return shad->get(set, SHADER_FT_SUB_REDUCE_TEX_REDUCE_4_EXTRACT); + } + break; + case 4: + switch (uniform_value[U_ALPHA_MASK]) { + case 0: + return shad->get(set, SHADER_FT_SUB_GHOST); + } + break; + case 5: + switch (uniform_value[U_ALPHA_MASK]) { + case 0: + return shad->get(set, SHADER_FT_SUB_REDUCE_TEX_REDUCE_COMPOSITE_2); + } + break; + case 6: + switch (uniform_value[U_ALPHA_MASK]) { + case 0: + return shad->get(set, SHADER_FT_SUB_REDUCE_TEX_REDUCE_COMPOSITE_BLUR); + } + break; + case 7: + switch (uniform_value[U_ALPHA_MASK]) { + case 0: + return shad->get(set, SHADER_FT_SUB_REDUCE_TEX_REDUCE_COMPOSITE_4); + } + break; + } + return {}; +} + +static shader_vulkan_pair shader_vulkan_get_magnify(shader_set_data* set, shader_vulkan* shad) { // Added + switch (uniform_value[U_MAGNIFY]) { + case 0: + return shad->get(set, SHADER_FT_SUB_MAGNIFY_LINEAR); + case 2: + return shad->get(set, SHADER_FT_SUB_MAGNIFY_DIFF); + case 3: + return shad->get(set, SHADER_FT_SUB_MAGNIFY_DIFF2); + case 4: + return shad->get(set, SHADER_FT_SUB_MAGNIFY_CONE); + case 5: + return shad->get(set, SHADER_FT_SUB_MAGNIFY_CONE2); + } + return {}; +} + +static shader_vulkan_pair shader_vulkan_get_mlaa(shader_set_data* set, shader_vulkan* shad) { // Added + switch (uniform_value[U_MLAA]) { + case 0: + return shad->get(set, SHADER_FT_SUB_MLAA_EDGE); + case 1: + return shad->get(set, SHADER_FT_SUB_MLAA_AREA); + case 2: + return shad->get(set, SHADER_FT_SUB_MLAA_BLEND); + } + return {}; +} + +static shader_vulkan_pair shader_vulkan_get_exposure(shader_set_data* set, shader_vulkan* shad) { // Added + switch (uniform_value[U_EXPOSURE]) { + case 0: + return shad->get(set, SHADER_FT_SUB_EXPOSURE_MINIFY); + case 1: + return shad->get(set, SHADER_FT_SUB_EXPOSURE_MEASURE); + case 2: + return shad->get(set, SHADER_FT_SUB_EXPOSURE_AVERAGE); + } + return {}; +} + +static shader_vulkan_pair shader_vulkan_get_gauss(shader_set_data* set, shader_vulkan* shad) { // Added + switch (uniform_value[U_GAUSS]) { + case 0: + return shad->get(set, SHADER_FT_SUB_PP_GAUSS_USUAL); + case 1: + return shad->get(set, SHADER_FT_SUB_PP_GAUSS_CONE); + } + return {}; +} + +static shader_vulkan_pair shader_vulkan_get_snow_particle(shader_set_data* set, shader_vulkan* shad) { // Added + switch (uniform_value[U_SNOW_PARTICLE]) { + case 0: + return shad->get(set, SHADER_FT_SUB_SNOW_PARTICLE_CPU); + case 1: + return shad->get(set, SHADER_FT_SUB_SNOW_PARTICLE); + } + return {}; +} + +static shader_vulkan_pair shader_vulkan_get_star(shader_set_data* set, shader_vulkan* shad) { // Added + switch (uniform_value[U_STAR]) { + case 0: + return shad->get(set, SHADER_FT_SUB_STAR); + case 1: + return shad->get(set, SHADER_FT_SUB_STAR_MILKY_WAY); + } + return {}; +} + +static shader_vulkan_pair shader_vulkan_get_imgfilter(shader_set_data* set, shader_vulkan* shad) { + switch (uniform_value[U_IMAGE_FILTER]) { + case 0: + return shad->get(set, SHADER_FT_SUB_BOX4); + case 1: + return shad->get(set, SHADER_FT_SUB_BOX8); + case 5: + return shad->get(set, SHADER_FT_SUB_COPY); + } + return {}; +} + +static shader_vulkan_pair shader_vulkan_get_dof(shader_set_data* set, shader_vulkan* shad) { // Added + switch (uniform_value[U_DOF_STAGE]) { + case 0: + return shad->get(set, SHADER_FT_SUB_DOF_RENDER_TILE); + case 1: + return shad->get(set, SHADER_FT_SUB_DOF_GATHER_TILE); + case 2: + return shad->get(set, SHADER_FT_SUB_DOF_DOWNSAMPLE); + case 3: + return shad->get(set, SHADER_FT_SUB_DOF_MAIN_FILTER); + case 4: + return shad->get(set, SHADER_FT_SUB_DOF_UPSAMPLE); + } + return {}; } diff --git a/src/CRE/shader_ft.hpp b/src/CRE/shader_ft.hpp index d7a98769..45a408ad 100644 --- a/src/CRE/shader_ft.hpp +++ b/src/CRE/shader_ft.hpp @@ -15,82 +15,77 @@ enum shader_ft_enum { SHADER_FT_STAGE, SHADER_FT_SKIN, SHADER_FT_SSS_SKIN, - //SHADER_FT_SSS_FILT, - SHADER_FT_SSS_FILT_MIN, - SHADER_FT_SSS_FILT_GAUSS_2D, + SHADER_FT_SSS_FILT, SHADER_FT_HAIR, SHADER_FT_CLOTH, SHADER_FT_TIGHTS, SHADER_FT_SKY, - SHADER_FT_EYEBALL, - SHADER_FT_EYELENS, + //SHADER_FT_EYEBALL, + //SHADER_FT_EYELENS, SHADER_FT_GLASEYE, - SHADER_FT_MEMBRAN, - SHADER_FT_SHDMAP, - SHADER_FT_ESM, + //SHADER_FT_MEMBRAN, + //SHADER_FT_SHDMAP, + //SHADER_FT_ESM, SHADER_FT_ESMGAUSS, - //SHADER_FT_ESMFILT, - SHADER_FT_ESMFILT_MIN, - SHADER_FT_ESMFILT_EROSION, + SHADER_FT_ESMFILT, SHADER_FT_LITPROJ, SHADER_FT_SIMPLE, SHADER_FT_SIL, SHADER_FT_LAMBERT, SHADER_FT_CONSTANT, - SHADER_FT_PEEL, + //SHADER_FT_PEEL, SHADER_FT_TONEMAP, SHADER_FT_REDUCE, SHADER_FT_MAGNIFY, - //SHADER_FT_MLAA, - SHADER_FT_MLAA_EDGE, - SHADER_FT_MLAA_AREA, - SHADER_FT_MLAA_BLEND, + SHADER_FT_MLAA, SHADER_FT_CONTOUR, SHADER_FT_CONTOUR_NPR, - //SHADER_FT_EXPOSURE, - SHADER_FT_EXPOSURE_MINIFY, - SHADER_FT_EXPOSURE_MEASURE, - SHADER_FT_EXPOSURE_AVERAGE, - //SHADER_FT_GAUSS, - SHADER_FT_GAUSS_USUAL, - SHADER_FT_GAUSS_CONE, + SHADER_FT_EXPOSURE, + SHADER_FT_GAUSS, SHADER_FT_SUN, - SHADER_FT_FADE, + SHADER_FT_SUN_NO_TEXTURED, + //SHADER_FT_FADE, SHADER_FT_WATER01, - SHADER_FT_WATER02, - SHADER_FT_WATRING, + //SHADER_FT_WATER02, + //SHADER_FT_WATRING, SHADER_FT_W_PTCL, SHADER_FT_SNOW_PT, SHADER_FT_LEAF_PT, SHADER_FT_STAR, - SHADER_FT_STAR_MILKY_WAY, - SHADER_FT_SNORING, - SHADER_FT_SN_FOOT, - SHADER_FT_SN_TSL, - SHADER_FT_SN_NRM, + //SHADER_FT_SNORING, + //SHADER_FT_SN_FOOT, + //SHADER_FT_SN_TSL, + //SHADER_FT_SN_NRM, SHADER_FT_FLOOR, SHADER_FT_PUDDLE, SHADER_FT_S_REFL, SHADER_FT_S_REFR, SHADER_FT_RIPEMIT, SHADER_FT_RAIN, - SHADER_FT_VOLLIT, - SHADER_FT_FENCE, + //SHADER_FT_VOLLIT, + //SHADER_FT_FENCE, SHADER_FT_RIPPLE, SHADER_FT_FOGPTCL, SHADER_FT_PARTICL, SHADER_FT_GLITTER_PT, - SHADER_FT_SHOWVEC, + //SHADER_FT_SHOW_VEC, SHADER_FT_FONT, SHADER_FT_MOVIE, SHADER_FT_IMGFILT, SHADER_FT_SPRITE, + SHADER_FT_DOF, // Added + SHADER_FT_GRID, // Added + SHADER_FT_TRANSPARENCY, // Added SHADER_FT_END, }; extern const shader_table shader_ft_table[]; extern const size_t shader_ft_table_size; -extern const shader_bind_func shader_ft_bind_func_table[]; -extern const size_t shader_ft_bind_func_table_size; +extern const shader_opengl_bind_func shader_ft_opengl_bind_func_table[]; +extern const size_t shader_ft_opengl_bind_func_table_size; +extern const shader_vulkan_get_func shader_ft_vulkan_get_func_table[]; +extern const size_t shader_ft_vulkan_get_func_table_size; extern shader_set_data shaders_ft; + +extern int32_t shader_ft_get_index_by_name(const char* name); diff --git a/src/CRE/shader_glsl.cpp b/src/CRE/shader_glsl.cpp deleted file mode 100644 index 92b84c92..00000000 --- a/src/CRE/shader_glsl.cpp +++ /dev/null @@ -1,562 +0,0 @@ -/* - by korenkonder - GitHub/GitLab: korenkonder -*/ - -#include "shader_glsl.hpp" -#include "../KKdLib/io/file_stream.hpp" -#include "../KKdLib/hash.hpp" -#include "../KKdLib/str_utils.hpp" -#include "gl_state.hpp" -#include "static_var.hpp" - -static const char* common_data_string = -"layout (binding = 0, std140) uniform common_data {\n" -" vec4 res; //x=width, y=height, z=1/width, w=1/height\n" -" mat4 vp;\n" -" mat4 view;\n" -" mat4 projection;\n" -" vec3 view_pos;\n" -"} cmn_data;\n" -"\n" -"#define COMMON_DATA_RES (cmn_data.res)\n" -"#define COMMON_DATA_VP (cmn_data.vp)\n" -"#define COMMON_DATA_VIEW (cmn_data.view)\n" -"#define COMMON_DATA_PROJ (cmn_data.projection)\n" -"#define COMMON_DATA_VIEW_POS (cmn_data.view_pos)"; - -static GLuint program_log = 0; - -static GLuint shader_compile(GLenum type, const char* data); -static GLint shader_get_uniform_block_index(GLint program, GLchar* name, - std::vector>* uniform_block); -static GLint shader_get_uniform_location(GLint program, GLchar* name, - std::vector>* uniform); -static char* shader_parse(char* data, const char* parse_string, const char* replace_string); -static char* shader_parse_define(char* data, shader_glsl_param* param); - -shader_glsl::shader_glsl() : program() { - -} - -shader_glsl::~shader_glsl() { - unload(); -} - -GLint shader_glsl::get_uniform_location(const char* name) { - if (program < 1) - return GL_INVALID_INDEX; - - return shader_get_uniform_location(program, (GLchar*)name, &uniform); -} - -void shader_glsl::load(const char* vert, const char* frag, - const char* geom, shader_glsl_param* param) { - if ((!frag && !vert && !geom) || !param) - return; - - uniform.clear(); - uniform_block.clear(); - - char* frag_data = str_utils_copy(frag); - char* vert_data = str_utils_copy(vert); - char* geom_data = str_utils_copy(geom); - - frag_data = shader_parse_define(frag_data, param); - vert_data = shader_parse_define(vert_data, param); - geom_data = shader_parse_define(geom_data, param); - - vert_data = shader_parse(vert_data, "//COMMONDATA", common_data_string); - frag_data = shader_parse(frag_data, "//COMMONDATA", common_data_string); - geom_data = shader_parse(geom_data, "//COMMONDATA", common_data_string); - - GLuint frag_shad = shader_compile(GL_FRAGMENT_SHADER, frag_data); - GLuint vert_shad = shader_compile(GL_VERTEX_SHADER, vert_data); - GLuint geom_shad = shader_compile(GL_GEOMETRY_SHADER, geom_data); - - free_def(frag_data); - free_def(vert_data); - free_def(geom_data); - - program = glCreateProgram(); - if (frag_shad) - glAttachShader(program, frag_shad); - if (vert_shad) - glAttachShader(program, vert_shad); - if (geom_shad) - glAttachShader(program, geom_shad); - glLinkProgram(program); - - GLint success = 0; - glGetProgramiv(program, GL_LINK_STATUS, &success); - if (!success) { - GLchar* info_log = force_malloc_s(GLchar, 0x10000); - glGetProgramInfoLog(program, 0x10000, 0, info_log); - printf_debug("Program linking error: %s\n%s\n", param->name, info_log); - free_def(info_log); - } - - if (frag_shad) - glDeleteShader(frag_shad); - if (vert_shad) - glDeleteShader(vert_shad); - if (geom_shad) - glDeleteShader(geom_shad); -} - -void shader_glsl::load_file(const char* vert_path, const char* frag_path, - const char* geom_path, shader_glsl_param* param) { - if ((!vert_path && !frag_path && !geom_path) || !param) - return; - - uniform.clear(); - uniform_block.clear(); - - file_stream st; - size_t l; - - char* vert = 0; - char* frag = 0; - char* geom = 0; - - st.open(vert_path, "rb"); - if (st.check_not_null()) { - l = st.length; - vert = force_malloc_s(char, l + 1); - st.read(vert, l); - vert[l] = 0; - } - st.close(); - - st.open(frag_path, "rb"); - if (st.check_not_null()) { - l = st.length; - frag = force_malloc_s(char, l + 1); - st.read(frag, l); - frag[l] = 0; - } - st.close(); - - st.open(geom_path, "rb"); - if (st.check_not_null()) { - l = st.length; - geom = force_malloc_s(char, l + 1); - st.read(geom, l); - geom[l] = 0; - } - st.close(); - - load(vert, frag, geom, param); - free_def(vert); - free_def(frag); -} - -void shader_glsl::load_file(const wchar_t* vert_path, const wchar_t* frag_path, - const wchar_t* geom_path, shader_glsl_param* param) { - if ((!vert_path && !frag_path && !geom_path) || !param) - return; - - uniform.clear(); - uniform_block.clear(); - - file_stream st; - size_t l; - - char* vert = 0; - char* frag = 0; - char* geom = 0; - - st.open(vert_path, L"rb"); - if (st.check_not_null()) { - l = st.length; - vert = force_malloc_s(char, l + 1); - st.read(vert, l); - vert[l] = 0; - } - st.close(); - - st.open(frag_path, L"rb"); - if (st.check_not_null()) { - l = st.length; - frag = force_malloc_s(char, l + 1); - st.read(frag, l); - frag[l] = 0; - } - st.close(); - - st.open(geom_path, L"rb"); - if (st.check_not_null()) { - l = st.length; - geom = force_malloc_s(char, l + 1); - st.read(geom, l); - geom[l] = 0; - } - st.close(); - - load(vert, frag, geom, param); - free_def(vert); - free_def(frag); -} - -void shader_glsl::set(const char* name, bool value) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniform1i(location, value ? 1 : 0); - } -} - -void shader_glsl::set(const char* name, int32_t value) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniform1i(location, value); - } -} - -void shader_glsl::set(const char* name, float_t value) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniform1f(location, value); - } -} - -void shader_glsl::set(const char* name, vec2i& value) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniform2i(location, value.x, value.y); - } -} - -void shader_glsl::set(const char* name, int32_t x, int32_t y) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniform2i(location, x, y); - } -} - -void shader_glsl::set(const char* name, vec2& value) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniform2f(location, value.x, value.y); - } -} - -void shader_glsl::set(const char* name, float_t x, float_t y) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniform2f(location, x, y); - } -} - -void shader_glsl::set(const char* name, vec3i& value) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniform3i(location, value.x, value.y, value.z); - } -} - -void shader_glsl::set(const char* name, int32_t x, int32_t y, int32_t z) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniform3i(location, x, y, z); - } -} - -void shader_glsl::set(const char* name, vec3& value) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniform3f(location, value.x, value.y, value.z); - } -} - -void shader_glsl::set(const char* name, float_t x, float_t y, float_t z) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniform3f(location, x, y, z); - } -} - -void shader_glsl::set(const char* name, vec4i& value) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniform4i(location, value.x, value.y, value.z, value.w); - } -} - -void shader_glsl::set(const char* name, int32_t x, int32_t y, int32_t z, int32_t w) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniform4i(location, x, y, z, w); - } -} - -void shader_glsl::set(const char* name, vec4& value) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniform4f(location, value.x, value.y, value.z, value.w); - } -} - -void shader_glsl::set(const char* name, float_t x, float_t y, float_t z, float_t w) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniform4f(location, x, y, z, w); - } -} - -void shader_glsl::set(const char* name, bool transpose, mat3& value) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniformMatrix3fv(location, 1, transpose, (GLfloat*)&value); - } -} - -void shader_glsl::set(const char* name, bool transpose, mat4& value) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniformMatrix4fv(location, 1, transpose, (GLfloat*)&value); - } -} - -void shader_glsl::set(const char* name, size_t count, int32_t* value) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniform1iv(location, (GLsizei)count, value); - } -} - -void shader_glsl::set(const char* name, size_t count, float_t* value) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniform1fv(location, (GLsizei)count, value); - } -} - -void shader_glsl::set(const char* name, size_t count, vec2i* value) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniform2iv(location, (GLsizei)count, (GLint*)value); - } -} - -void shader_glsl::set(const char* name, size_t count, vec2* value) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniform2fv(location, (GLsizei)count, (GLfloat*)value); - } -} - -void shader_glsl::set(const char* name, size_t count, vec3i* value) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniform3iv(location, (GLsizei)count, (GLint*)value); - } -} - -void shader_glsl::set(const char* name, size_t count, vec3* value) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniform3fv(location, (GLsizei)count, (GLfloat*)value); - } -} - -void shader_glsl::set(const char* name, size_t count, vec4i* value) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniform4iv(location, (GLsizei)count, (GLint*)value); - } -} - -void shader_glsl::set(const char* name, size_t count, vec4* value) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniform4fv(location, (GLsizei)count, (GLfloat*)value); - } -} - -void shader_glsl::set(const char* name, size_t count, bool transpose, mat3* value) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniformMatrix3fv(location, (GLsizei)count, transpose, (GLfloat*)value); - } -} - -void shader_glsl::set(const char* name, size_t count, bool transpose, mat4* value) { - int32_t location = get_uniform_location(name); - if (location != GL_INVALID_INDEX) { - use(); - glUniformMatrix4fv(location, (GLsizei)count, transpose, (GLfloat*)value); - } -} - -void shader_glsl::set_uniform_block_binding(GLchar* name, GLint binding) { - GLint index = shader_get_uniform_block_index(program, name, &uniform_block); - if (index != GL_INVALID_INDEX) - glUniformBlockBinding(program, index, binding); -} - -void shader_glsl::unload() { - if (program) { - glDeleteProgram(program); - program = 0; - } -} - -void shader_glsl::use() { - gl_state_use_program(program); -} - -static GLuint shader_compile(GLenum type, const char* data) { - if (!data) - return 0; - - GLuint shader = glCreateShader(type); - glShaderSource(shader, 1, &data, 0); - glCompileShader(shader); - - GLint success = 0; - glGetShaderiv(shader, GL_COMPILE_STATUS, &success); - if (!success) { - GLchar* info_log = force_malloc_s(GLchar, 0x10000); - glGetShaderInfoLog(shader, 0x10000, 0, info_log); - printf_debug("Shader compile error:\n%s\n", info_log); - free_def(info_log); - } - return shader; -} - -static GLint shader_get_uniform_block_index(GLint program, GLchar* name, - std::vector>* uniform_block) { - uint64_t hash = hash_utf8_fnv1a64m(name); - for (std::pair& i : *uniform_block) - if (i.first == hash) - return i.second; - - GLint index = glGetUniformBlockIndex(program, name); - if (index == GL_INVALID_INDEX) { - if (program_log != program) { - printf_debug("Program %d:\n", program); - program_log = program; - } - printf_debug(" Block Index for \"%s\" not found\n", name); - } - uniform_block->push_back({ hash, index }); - return index; -} - -static GLint shader_get_uniform_location(GLint program, GLchar* name, - std::vector>* uniform) { - uint64_t hash = hash_utf8_fnv1a64m(name); - for (std::pair& i : *uniform) - if (i.first == hash) - return i.second; - - GLint location = glGetUniformLocation(program, name); - if (location == GL_INVALID_INDEX) { - if (program_log != program) { - printf_debug("Program %d:\n", program); - program_log = program; - } - printf_debug(" Location for \"%s\" not found\n", name); - } - uniform->push_back({ hash, location }); - return location; - -} - -static char* shader_parse(char* data, const char* parse_string, const char* replace_string) { - if (!data) - return data; - - char* def = strstr(data, parse_string); - if (!def) - return data; - - size_t len_a = def - data; - def += utf8_length(parse_string); - size_t len_b = utf8_length(replace_string); - size_t len_c = utf8_length(def); - size_t len = len_a + len_b + len_c; - - char* temp_data = force_malloc_s(char, len + 1); - size_t pos = 0; - memcpy(temp_data + pos, data, len_a); - pos += len_a; - memcpy(temp_data + pos, replace_string, len_b); - pos += len_b; - memcpy(temp_data + pos, def, len_c); - free_def(data); - return temp_data; -} - -static char* shader_parse_define(char* data, shader_glsl_param* param) { - if (!data || !param) - return data; - - char* def = strstr(data, "//DEF\n"); - if (!def) - return data; - - const size_t s = SHADER_PARAM_NUM_PARAMS; - - size_t temp_len[SHADER_PARAM_NUM_PARAMS]; - char temp[0x100] = {}; - - for (size_t i = 0; i < s; i++) - if (param->param[i]) { - sprintf_s(temp, sizeof(temp), "#define %s\n", param->param[i]); - temp_len[i] = utf8_length(temp); - } - else - temp_len[i] = 0; - - size_t len_a = def - data; - def += 5; - size_t len_b = utf8_length(def); - size_t len = 0; - for (size_t i = 0; i < s; i++) - len += temp_len[i]; - if (!len) - def++; - len += len_a + len_b; - - char* temp_data = force_malloc_s(char, len + 1); - size_t pos = 0; - memcpy(temp_data + pos, data, len_a); - pos += len_a; - for (size_t i = 0; i < s; i++) { - if (param->param[i]) { - sprintf_s(temp, sizeof(temp), "#define %s\n", param->param[i]); - memcpy(temp_data + pos, temp, temp_len[i]); - } - pos += temp_len[i]; - } - memcpy(temp_data + pos, def, len_b); - free_def(data); - return temp_data; -} diff --git a/src/CRE/shader_glsl.hpp b/src/CRE/shader_glsl.hpp deleted file mode 100644 index cc507275..00000000 --- a/src/CRE/shader_glsl.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - by korenkonder - GitHub/GitLab: korenkonder -*/ - -#pragma once - -#include -#include -#include "../KKdLib/default.hpp" -#include "../KKdLib/farc.hpp" -#include "../KKdLib/mat.hpp" -#include "../KKdLib/vec.hpp" -#include - -#define SHADER_PARAM_NUM_PARAMS 12 - -struct shader_glsl_param { - const char* name; - char* frag; - char* vert; - char* param[SHADER_PARAM_NUM_PARAMS]; -}; - -struct shader_glsl { - std::string name; - GLuint program; - std::vector> uniform; - std::vector> uniform_block; - - shader_glsl(); - ~shader_glsl(); - - GLint get_uniform_location(const char* name); - void load(const char* vert_data, const char* frag_data, - const char* geom_data, shader_glsl_param* param); - void load_file(const char* vert_path, const char* frag_path, - const char* geom_path, shader_glsl_param* param); - void load_file(const wchar_t* vert_path, const wchar_t* frag_path, - const wchar_t* geom_path, shader_glsl_param* param); - void set(const char* name, bool value); - void set(const char* name, int32_t value); - void set(const char* name, float_t value); - void set(const char* name, vec2i& value); - void set(const char* name, int32_t x, int32_t y); - void set(const char* name, vec2& value); - void set(const char* name, float_t x, float_t y); - void set(const char* name, vec3i& value); - void set(const char* name, int32_t x, int32_t y, int32_t z); - void set(const char* name, vec3& value); - void set(const char* name, float_t x, float_t y, float_t z); - void set(const char* name, vec4i& value); - void set(const char* name, int32_t x, int32_t y, int32_t z, int32_t w); - void set(const char* name, vec4& value); - void set(const char* name, float_t x, float_t y, float_t z, float_t w); - void set(const char* name, bool transpose, mat3& value); - void set(const char* name, bool transpose, mat4& value); - void set(const char* name, size_t count, int32_t* value); - void set(const char* name, size_t count, float_t* value); - void set(const char* name, size_t count, vec2i* value); - void set(const char* name, size_t count, vec2* value); - void set(const char* name, size_t count, vec3i* value); - void set(const char* name, size_t count, vec3* value); - void set(const char* name, size_t count, vec4i* value); - void set(const char* name, size_t count, vec4* value); - void set(const char* name, size_t count, bool transpose, mat3* value); - void set(const char* name, size_t count, bool transpose, mat4* value); - void set_uniform_block_binding(GLchar* name, GLint binding); - void unload(); - void use(); -}; diff --git a/src/CRE/sound.cpp b/src/CRE/sound.cpp index f23eb8de..4ac91a79 100644 --- a/src/CRE/sound.cpp +++ b/src/CRE/sound.cpp @@ -7,10 +7,10 @@ #include "../KKdLib/io/file_stream.hpp" #include "../KKdLib/key_val.hpp" #include "../KKdLib/str_utils.hpp" +#include "../KKdLib/time.hpp" #include "../KKdLib/vec.hpp" #include "data.hpp" #include "ogg_vorbis.hpp" -#include "timer.hpp" #include #include diff --git a/src/CRE/sound.hpp b/src/CRE/sound.hpp index 3f80c54c..37e3192d 100644 --- a/src/CRE/sound.hpp +++ b/src/CRE/sound.hpp @@ -6,10 +6,10 @@ #pragma once #include "../KKdLib/default.hpp" +#include "../KKdLib/time.hpp" #include "../KKdLib/farc.hpp" #include "file_handler.hpp" #include "lock.hpp" -#include "time.hpp" #include #include #include diff --git a/src/CRE/stage.cpp b/src/CRE/stage.cpp index 41c8c090..05033a5e 100644 --- a/src/CRE/stage.cpp +++ b/src/CRE/stage.cpp @@ -8,7 +8,6 @@ #include "../KKdLib/str_utils.hpp" #include "../KKdLib/vec.hpp" #include "rob/rob.hpp" -#include "draw_task.hpp" #include "light_param.hpp" #include "render_context.hpp" #include "task_effect.hpp" @@ -32,9 +31,9 @@ namespace stage_detail { static void TaskStage_Unload(stage_detail::TaskStage* a1); } -static bool object_bounding_sphere_check_visibility_shadow(obj_bounding_sphere* sphere, camera* cam, mat4* mat); -static bool object_bounding_sphere_check_visibility_shadow_chara(obj_bounding_sphere* sphere, camera* cam); -static bool object_bounding_sphere_check_visibility_shadow_stage(obj_bounding_sphere* sphere, camera* cam); +static bool object_bounding_sphere_check_visibility_shadow(obj_bounding_sphere* sphere, mat4* view, mat4* mat); +static bool object_bounding_sphere_check_visibility_shadow_chara(obj_bounding_sphere* sphere, mat4* view); +static bool object_bounding_sphere_check_visibility_shadow_stage(obj_bounding_sphere* sphere, mat4* view); static bool stage_ctrl(stage* s); static void stage_disp(stage* s); @@ -47,40 +46,62 @@ static void stage_set(stage* s, stage* other); static void stage_set_by_stage_index(stage* s, int32_t stage_index, uint16_t stage_counter); stage_detail::TaskStage* task_stage; -DtmStg* dtm_stg; extern render_context* rctx_ptr; extern bool task_stage_is_modern; extern bool light_chara_ambient; -extern vec4 npr_spec_color; +extern vec4 npr_cloth_spec_color; static uint16_t stage_counter; -void dtm_stg_init() { - dtm_stg = new DtmStg; +stage::stage() : index(), counter(), state(), stage_data(), stage_display(), +lens_flare(), ground(), ring(), sky(), auth_3d_loaded(), mat(), rot_y(), obj_set() { + } -void dtm_stg_load(int32_t stage_index) { - if (app::TaskWork::CheckTaskReady(dtm_stg)) +stage_detail::TaskStage::TaskStage() : state(), current_stage(), stage_display(), +field_FB1(), field_FB2(), field_FB3(), field_FB4(), mat(), field_FF8() { + +} + +stage_detail::TaskStage:: ~TaskStage() { + +} + +bool stage_detail::TaskStage::Init() { + return true; +} + +bool stage_detail::TaskStage::Ctrl() { + stage_detail::TaskStage_CtrlInner(this); + + for (int32_t i = 0; i < TASK_STAGE_STAGE_COUNT; i++) + if (stages[i].index != -1 && stage_ctrl(&stages[i])) + break; + return false; +} + +bool stage_detail::TaskStage::Dest() { + stage_detail::TaskStage_Unload(this); + if (state) + return false; + stage_detail::TaskStage_Reset(this); + return true; +} + +void stage_detail::TaskStage::Disp() { + if (state != 6 || !stage_display) return; - if (app::TaskWork::CheckTaskReady(dtm_stg)) { - dtm_stg->stage_index = stage_index; - dtm_stg->load_stage_index = stage_index; - } - app::TaskWork::AppendTask(dtm_stg, "DATA_TEST_STAGE"); + stage* s = stage_detail::TaskStage_GetCurrentStage(this); + if (s) + stage_disp(s); } -bool dtm_stg_unload() { - return dtm_stg->SetDest(); -} - -void dtm_stg_free() { - if (dtm_stg) { - delete dtm_stg; - dtm_stg = 0; - } +task_stage_info::task_stage_info() { + load_index = -1; + load_counter = 0; } void task_stage_init() { @@ -91,13 +112,6 @@ bool task_stage_check_not_loaded() { return task_stage->load_stage_indices.size() || task_stage->state != 6; } -void task_stage_current_set_effect_display(bool value) { - task_stage_info stg_info; - task_stage_get_current_stage_info(&stg_info); - if (task_stage_has_stage_info(&stg_info)) - task_stage_set_effect_display(&stg_info, value); -} - void task_stage_current_set_ground(bool value) { task_stage_info stg_info; task_stage_get_current_stage_info(&stg_info); @@ -170,12 +184,6 @@ bool task_stage_load_task(const char* name) { return stage_detail::TaskStage_LoadTask(task_stage, name); } -void task_stage_set_effect_display(task_stage_info* stg_info, bool value) { - stage* stg = task_stage_get_stage(*stg_info); - if (stg) - stg->effect_display = value; -} - void task_stage_set_ground(task_stage_info* stg_info, bool value) { stage* stg = task_stage_get_stage(*stg_info); if (stg) @@ -230,7 +238,7 @@ void task_stage_set_stage_indices(std::vector& stage_indices) { } bool task_stage_unload_task() { - return task_stage->SetDest(); + return task_stage->DelTask(); } void task_stage_free() { @@ -346,14 +354,14 @@ static void stage_detail::TaskStage_Load(stage_detail::TaskStage* a1) { } static bool stage_detail::TaskStage_LoadTask(stage_detail::TaskStage* a1, const char* name) { - if (app::TaskWork::AppendTask(a1, name)) { + if (app::TaskWork::AddTask(a1, name)) { stage_detail::TaskStage_Reset(a1); stage_detail::TaskStage_TaskWindAppend(a1); return false; } else { if (!app::TaskWork::HasTaskDest(a1)) - a1->SetDest(); + a1->DelTask(); return true; } } @@ -383,7 +391,7 @@ static void stage_detail::TaskStage_SetStage(stage_detail::TaskStage* a1, task_s } static void stage_detail::TaskStage_TaskWindAppend(stage_detail::TaskStage* a1) { - app::TaskWork::AppendTask(task_wind, a1, "CHARA WIND"); + app::TaskWork::AddTask(task_wind, a1, "CHARA WIND"); } static void stage_detail::TaskStage_Unload(stage_detail::TaskStage* a1) { @@ -429,13 +437,13 @@ static void stage_detail::TaskStage_Unload(stage_detail::TaskStage* a1) { } } -static bool object_bounding_sphere_check_visibility_shadow(obj_bounding_sphere* sphere, camera* cam, mat4* mat) { +static bool object_bounding_sphere_check_visibility_shadow(obj_bounding_sphere* sphere, mat4* view, mat4* mat) { vec3 center; mat4_mult_vec3_trans(mat, &sphere->center, ¢er); - mat4_mult_vec3_trans(&cam->view, ¢er, ¢er); + mat4_mult_vec3_trans(view, ¢er, ¢er); float_t radius = sphere->radius; - shadow* shad = rctx_ptr->draw_pass.shadow_ptr; + shadow* shad = rctx_ptr->render_manager.shadow_ptr; float_t v9 = shad->view_region * shad->range; if ((center.x + radius) < -v9 || (center.x - radius) > v9 @@ -447,18 +455,18 @@ static bool object_bounding_sphere_check_visibility_shadow(obj_bounding_sphere* return true; } -static bool object_bounding_sphere_check_visibility_shadow_chara(obj_bounding_sphere* sphere, camera* cam) { +static bool object_bounding_sphere_check_visibility_shadow_chara(obj_bounding_sphere* sphere, mat4* view) { mat4 mat; - shadow* shad = rctx_ptr->draw_pass.shadow_ptr; + shadow* shad = rctx_ptr->render_manager.shadow_ptr; mat4_look_at(&shad->view_point[0], &shad->interest[0], &mat); - return object_bounding_sphere_check_visibility_shadow(sphere, cam, &mat); + return object_bounding_sphere_check_visibility_shadow(sphere, view, &mat); } -static bool object_bounding_sphere_check_visibility_shadow_stage(obj_bounding_sphere* sphere, camera* cam) { +static bool object_bounding_sphere_check_visibility_shadow_stage(obj_bounding_sphere* sphere, mat4* view) { mat4 mat; - shadow* shad = rctx_ptr->draw_pass.shadow_ptr; + shadow* shad = rctx_ptr->render_manager.shadow_ptr; mat4_look_at(&shad->view_point[1], &shad->interest[1], &mat); - return object_bounding_sphere_check_visibility_shadow(sphere, cam, &mat); + return object_bounding_sphere_check_visibility_shadow(sphere, view, &mat); } static bool stage_ctrl(stage* s) { @@ -470,14 +478,6 @@ static bool stage_ctrl(stage* s) { stage_free(s); return true; } - - for (auth_3d_id& i : s->auth_3d_ids) { - i.set_repeat(true); - i.set_paused(false); - i.set_enable(true); - i.set_visibility(s->effect_display); - i.set_frame_rate(0); - } return false; } @@ -485,40 +485,35 @@ static void stage_disp(stage* s) { if (s->state != 6 || !s->stage_display) return; - object_data* object_data = &rctx_ptr->object_data; + mdl::DispManager& disp_manager = rctx_ptr->disp_manager; mat4 mat; mat4_rotate_y(s->rot_y, &mat); if (s->stage_data->object_ground.not_null() && s->ground) - draw_task_add_draw_object_by_object_info(rctx_ptr, - &mat, s->stage_data->object_ground, 0, 0, 0, 0, 0, 0); + disp_manager.entry_obj_by_object_info(&mat, s->stage_data->object_ground); if (s->stage_data->object_ring.not_null() && s->ring) - draw_task_add_draw_object_by_object_info(rctx_ptr, - &mat, s->stage_data->object_ring, 0, 0, 0, 0, 0, 0); + disp_manager.entry_obj_by_object_info(&mat, s->stage_data->object_ring); if (s->stage_data->object_reflect.not_null()) { - object_data->set_draw_task_flags( + disp_manager.set_draw_task_flags( (draw_task_flags)(DRAW_TASK_NO_TRANSLUCENCY | DRAW_TASK_REFLECT)); - draw_task_add_draw_object_by_object_info(rctx_ptr, - &mat, s->stage_data->object_reflect, 0, 0, 0, 0, 0, 0); - object_data->set_draw_task_flags(); + disp_manager.entry_obj_by_object_info(&mat, s->stage_data->object_reflect); + disp_manager.set_draw_task_flags(); } if (s->stage_data->object_refract.not_null()) { - object_data->set_draw_task_flags( + disp_manager.set_draw_task_flags( (draw_task_flags)(DRAW_TASK_NO_TRANSLUCENCY | DRAW_TASK_REFRACT)); - draw_task_add_draw_object_by_object_info(rctx_ptr, - &mat, s->stage_data->object_refract, 0, 0, 0, 0, 0, 0); - object_data->set_draw_task_flags(); + disp_manager.entry_obj_by_object_info(&mat, s->stage_data->object_refract); + disp_manager.set_draw_task_flags(); } if (s->stage_data->object_sky.not_null() && s->sky) { mat4 t = s->mat; mat4_mult(&t, &mat, &t); - draw_task_add_draw_object_by_object_info(rctx_ptr, - &t, s->stage_data->object_sky, 0, 0, 0, 0, 0, 0); + disp_manager.entry_obj_by_object_info(&t, s->stage_data->object_sky); } if (s->stage_data->lens_flare_texture != -1 && s->lens_flare) { @@ -546,21 +541,21 @@ static void stage_disp_shadow(stage* s) { } static void stage_disp_shadow_object(object_info object, mat4* mat) { - object_data* object_data = &rctx_ptr->object_data; + mdl::DispManager& disp_manager = rctx_ptr->disp_manager; for (int32_t i = SHADOW_CHARA; i < SHADOW_MAX; i++) { - object_data->set_shadow_type((shadow_type_enum)i); - object_data->set_object_bounding_sphere_check_func(i == SHADOW_CHARA + disp_manager.set_shadow_type((shadow_type_enum)i); + disp_manager.set_culling_finc(i == SHADOW_CHARA ? object_bounding_sphere_check_visibility_shadow_chara : object_bounding_sphere_check_visibility_shadow_stage); - object_data->set_draw_task_flags( + disp_manager.set_draw_task_flags( (draw_task_flags)(DRAW_TASK_NO_TRANSLUCENCY | DRAW_TASK_SHADOW_OBJECT)); - draw_task_add_draw_object_by_object_info(rctx_ptr, mat, object, 0, 0, 0, 0, 0, 0); + disp_manager.entry_obj_by_object_info(mat, object); } - object_data->set_draw_task_flags(); - object_data->set_object_bounding_sphere_check_func(); - object_data->set_shadow_type(); + disp_manager.set_draw_task_flags(); + disp_manager.set_culling_finc(); + disp_manager.set_shadow_type(); } static void stage_free(stage* s) { @@ -573,7 +568,7 @@ static void stage_free(stage* s) { if (s->state < 7 || s->state > 9) return; - draw_pass* draw_pass = &rctx_ptr->draw_pass; + rndr::RenderManager* render_manager = &rctx_ptr->render_manager; if (s->stage_data->render_texture != -1) rctx_ptr->post_process.render_texture_free( texture_storage_get_texture(s->stage_data->render_texture), 0); @@ -582,14 +577,14 @@ static void stage_free(stage* s) { rctx_ptr->post_process.movie_texture_free( texture_storage_get_texture(s->stage_data->movie_texture)); - draw_pass->set_shadow_true(); + render_manager->set_shadow_true(); //rctx_ptr->post_process.field_14 = 0; //rctx_ptr->post_process.field_ED8 = 1; - npr_spec_color.w = 1.0f; - draw_pass->field_31D = 0; - draw_pass->field_31E = 0; - draw_pass->field_31F = false; - draw_pass->field_320 = 0; + npr_cloth_spec_color.w = 1.0f; + render_manager->field_31D = false; + render_manager->field_31E = false; + render_manager->field_31F = false; + render_manager->field_320 = false; light_chara_ambient = false; if (s->auth_3d_loaded) { @@ -660,71 +655,65 @@ static void stage_reset(stage* s) { s->mat = mat4_identity; s->rot_y = 0.0; s->obj_set = -1; - - for (auth_3d_id& i : s->auth_3d_ids) - i.unload_id(rctx_ptr); - s->auth_3d_ids.clear(); - s->auth_3d_ids.shrink_to_fit(); - s->effect_display = true; } static void stage_set(stage* s, stage* other) { if (other && s == other) return; - draw_pass* draw_pass = &rctx_ptr->draw_pass; + rndr::RenderManager* render_manager = &rctx_ptr->render_manager; if (s) { //flt_14CC925C0 = 0.0; //flt_140CB3704 = -1001.0; - draw_pass->set_enable(DRAW_PASS_REFLECT, false); - draw_pass->set_reflect(false); - draw_pass->set_enable(DRAW_PASS_REFRACT, false); - draw_pass->set_refract(true); - draw_pass->field_31D = false; - draw_pass->field_31E = false; - draw_pass->field_31F = false; - draw_pass->field_320 = false; - draw_pass->set_shadow_true(); + render_manager->set_pass_sw(rndr::RND_PASSID_REFLECT, false); + render_manager->set_reflect(false); + render_manager->set_pass_sw(rndr::RND_PASSID_REFRACT, false); + render_manager->set_refract(true); + render_manager->field_31D = false; + render_manager->field_31E = false; + render_manager->field_31F = false; + render_manager->field_320 = false; + render_manager->set_shadow_true(); //rctx_ptr->post_process.field_14 = 0; //rctx_ptr->post_process.field_ED8 = 1; - npr_spec_color.w = 1.0f; - draw_pass->set_npr_param(0); + npr_cloth_spec_color.w = 1.0f; + render_manager->set_npr_param(0); light_chara_ambient = false; } if (other) { rctx_ptr->chara_reflect = other->stage_data->reflect_type != STAGE_DATA_REFLECT_DISABLE; - draw_pass->set_reflect_type(other->stage_data->reflect_type); + render_manager->set_reflect_type(other->stage_data->reflect_type); rctx_ptr->chara_refract = other->stage_data->refract_enable; if (other->stage_data->reflect) { stage_data_reflect* reflect = &other->stage_data->reflect_data; - draw_pass->set_enable(DRAW_PASS_REFLECT, true); - draw_pass->set_reflect_blur(reflect->blur_num, (blur_filter_mode)reflect->blur_filter); - draw_pass->set_reflect(true); + render_manager->set_pass_sw(rndr::RND_PASSID_REFLECT, true); + render_manager->set_reflect_blur(reflect->blur_num, (blur_filter_mode)reflect->blur_filter); + render_manager->set_reflect(true); reflect_refract_resolution_mode reflect_resolution_mode = REFLECT_REFRACT_RESOLUTION_512x512; if (other->stage_data->reflect_type != STAGE_DATA_REFLECT_REFLECT_MAP) reflect_resolution_mode = (reflect_refract_resolution_mode)reflect->mode; - draw_pass->set_refract_resolution_mode(reflect_resolution_mode); + render_manager->set_refract_resolution_mode(reflect_resolution_mode); } if (other->stage_data->refract) { stage_data_refract* refract = &other->stage_data->refract_data; - draw_pass->set_enable(DRAW_PASS_REFRACT, true); - draw_pass->set_refract_resolution_mode((reflect_refract_resolution_mode)refract->mode); - draw_pass->set_refract(true); + render_manager->set_pass_sw(rndr::RND_PASSID_REFRACT, true); + render_manager->set_refract_resolution_mode((reflect_refract_resolution_mode)refract->mode); + render_manager->set_refract(true); } if (other->stage_data->flags & STAGE_DATA_FLAG_1) - draw_pass->field_31D = true; + render_manager->field_31D = true; if (other->stage_data->flags & STAGE_DATA_FLAG_4) - draw_pass->field_31E = true; + render_manager->field_31E = true; if (other->stage_data->flags & STAGE_DATA_FLAG_8) - draw_pass->field_320 = true; + render_manager->field_320 = true; if (other->stage_data->flags & STAGE_DATA_LIGHT_CHARA_AMBIENT) light_chara_ambient = true; //if (stru_14CC92630.pv_id == 421) - //draw_pass->field_31F = true; + //render_manager->field_31F = true; //sub_14064DC10(); task_effect_parent_set_current_stage_index(other->index); } @@ -783,87 +772,3 @@ static void stage_set_by_stage_index(stage* s, int32_t stage_index, uint16_t sta if (set_name[0]) s->obj_set = aft_obj_db->get_object_set_id(set_name); } - -stage::stage() : index(), counter(), state(), stage_data(), stage_display(), lens_flare(), -ground(), ring(), sky(), auth_3d_loaded(), mat(), rot_y(), obj_set(), effect_display() { - -} - -stage::~stage() { - -} - -stage_detail::TaskStage::TaskStage() : state(), current_stage(), stage_display(), -field_FB1(), field_FB2(), field_FB3(), field_FB4(), mat(), field_FF8() { - -} - -stage_detail::TaskStage:: ~TaskStage() { - -} - -bool stage_detail::TaskStage::Init() { - return true; -} - -bool stage_detail::TaskStage::Ctrl() { - stage_detail::TaskStage_CtrlInner(this); - - for (int32_t i = 0; i < TASK_STAGE_STAGE_COUNT; i++) - if (stages[i].index != -1 && stage_ctrl(&stages[i])) - break; - return false; -} - -bool stage_detail::TaskStage::Dest() { - stage_detail::TaskStage_Unload(this); - if (state) - return false; - stage_detail::TaskStage_Reset(this); - return true; -} - -void stage_detail::TaskStage::Disp() { - if (state != 6 || !stage_display) - return; - - stage* s = stage_detail::TaskStage_GetCurrentStage(this); - if (s) - stage_disp(s); -} - -task_stage_info::task_stage_info() { - load_index = -1; - load_counter = 0; -} - -DtmStg::DtmStg() : stage_index(), load_stage_index() { - -} - -DtmStg::~DtmStg() { - -} - -bool DtmStg::Init() { - task_stage_load_task("DATA_TEST_STG_STAGE"); - task_stage_set_stage_index(stage_index); - return true; -} - -bool DtmStg::Ctrl() { - if (task_stage_check_not_loaded()) - return false; - - if (load_stage_index != stage_index) { - task_stage_set_stage_index(load_stage_index); - stage_index = load_stage_index; - return false; - } - return false; -} - -bool DtmStg::Dest() { - task_stage_unload_task(); - return true; -} diff --git a/src/CRE/stage.hpp b/src/CRE/stage.hpp index cb84423b..e21e7215 100644 --- a/src/CRE/stage.hpp +++ b/src/CRE/stage.hpp @@ -26,12 +26,7 @@ struct stage { float_t rot_y; uint32_t obj_set; - // Temp - std::vector auth_3d_ids; - bool effect_display; - stage(); - ~stage(); }; #define TASK_STAGE_STAGE_COUNT 37 @@ -69,29 +64,8 @@ struct task_stage_info { task_stage_info(); }; -class DtmStg : public app::Task { -public: - int32_t stage_index; - int32_t load_stage_index; - - DtmStg(); - virtual ~DtmStg() override; - - virtual bool Init() override; - virtual bool Ctrl() override; - virtual bool Dest() override; -}; - -extern DtmStg* dtm_stg; - -extern void dtm_stg_init(); -extern void dtm_stg_load(int32_t stage_index); -extern bool dtm_stg_unload(); -extern void dtm_stg_free(); - extern void task_stage_init(); extern bool task_stage_check_not_loaded(); -extern void task_stage_current_set_effect_display(bool value); extern void task_stage_current_set_ground(bool value); extern void task_stage_current_set_ring(bool value); extern void task_stage_current_set_sky(bool value); @@ -105,7 +79,6 @@ extern stage* task_stage_get_stage(task_stage_info stg_info); int32_t task_stage_get_stage_index(task_stage_info* stg_info); extern bool task_stage_has_stage_info(task_stage_info* stg_info); extern bool task_stage_load_task(const char* name); -extern void task_stage_set_effect_display(task_stage_info* stg_info, bool value); extern void task_stage_set_ground(task_stage_info* stg_info, bool value); extern void task_stage_set_mat(mat4* mat); extern void task_stage_set_ring(task_stage_info* stg_info, bool value); diff --git a/src/CRE/stage_modern.cpp b/src/CRE/stage_modern.cpp index 96424854..e1a29920 100644 --- a/src/CRE/stage_modern.cpp +++ b/src/CRE/stage_modern.cpp @@ -8,7 +8,6 @@ #include "../KKdLib/str_utils.hpp" #include "../KKdLib/vec.hpp" #include "rob/rob.hpp" -#include "draw_task.hpp" #include "light_param.hpp" #include "render_context.hpp" #include "task_effect.hpp" @@ -32,9 +31,9 @@ namespace stage_detail { static void TaskStageModern_Unload(stage_detail::TaskStageModern* a1); } -static bool object_bounding_sphere_check_visibility_shadow(obj_bounding_sphere* sphere, camera* cam, mat4* mat); -static bool object_bounding_sphere_check_visibility_shadow_chara(obj_bounding_sphere* sphere, camera* cam); -static bool object_bounding_sphere_check_visibility_shadow_stage(obj_bounding_sphere* sphere, camera* cam); +static bool object_bounding_sphere_check_visibility_shadow(obj_bounding_sphere* sphere, mat4* view, mat4* mat); +static bool object_bounding_sphere_check_visibility_shadow_chara(obj_bounding_sphere* sphere, mat4* view); +static bool object_bounding_sphere_check_visibility_shadow_stage(obj_bounding_sphere* sphere, mat4* view); static bool stage_modern_ctrl(stage_modern* s, void* data, object_database* obj_db, texture_database* tex_db); static void stage_modern_disp(stage_modern* s); @@ -53,10 +52,61 @@ extern render_context* rctx_ptr; extern bool task_stage_is_modern; extern bool light_chara_ambient; -extern vec4 npr_spec_color; +extern vec4 npr_cloth_spec_color; static uint16_t stage_counter; +stage_modern::stage_modern() : counter(), state(), stage_data(), +stage_display(), ground(), sky(), auth_3d_loaded(), mat(), rot_y() { + hash = hash_murmurhash_empty; + obj_set_hash = hash_murmurhash_empty; +} + +stage_detail::TaskStageModern::TaskStageModern() : state(), current_stage(), stage_display(), field_FB1(), +field_FB2(), field_FB3(), field_FB4(), mat(), field_FF8(), data(), obj_db(), tex_db(), stage_data() { + +} + +stage_detail::TaskStageModern:: ~TaskStageModern() { + +} + +bool stage_detail::TaskStageModern::Init() { + return true; +} + +bool stage_detail::TaskStageModern::Ctrl() { + stage_detail::TaskStageModern_CtrlInner(this); + + for (int32_t i = 0; i < TASK_STAGE_STAGE_COUNT; i++) + if (stages[i].hash != -1 && stages[i].hash != hash_murmurhash_empty + && stage_modern_ctrl(&stages[i], data, obj_db, tex_db)) + break; + return false; +} + +bool stage_detail::TaskStageModern::Dest() { + stage_detail::TaskStageModern_Unload(this); + if (state) + return false; + stage_detail::TaskStageModern_Reset(this); + return true; +} + +void stage_detail::TaskStageModern::Disp() { + if (state != 6 || !stage_display) + return; + + stage_modern* s = stage_detail::TaskStageModern_GetCurrentStage(this); + if (s) + stage_modern_disp(s); +} + +task_stage_modern_info::task_stage_modern_info() { + load_index = -1; + load_counter = 0; +} + void task_stage_modern_init() { task_stage_modern = new stage_detail::TaskStageModern; } @@ -69,13 +119,6 @@ bool task_stage_modern_check_task_ready() { return app::TaskWork::CheckTaskReady(task_stage_modern); } -void task_stage_modern_current_set_effect_display(bool value) { - task_stage_modern_info stg_info; - task_stage_modern_get_current_stage_info(&stg_info); - if (task_stage_modern_has_stage_info(&stg_info)) - task_stage_modern_set_effect_display(&stg_info, value); -} - void task_stage_modern_current_set_ground(bool value) { task_stage_modern_info stg_info; task_stage_modern_get_current_stage_info(&stg_info); @@ -149,12 +192,6 @@ void task_stage_modern_set_data(void* data, task_stage_modern->stage_data = stage_data; } -void task_stage_modern_set_effect_display(task_stage_modern_info* stg_info, bool value) { - stage_modern* stg = task_stage_modern_get_stage(*stg_info); - if (stg) - stg->effect_display = value; -} - void task_stage_modern_set_ground(task_stage_modern_info* stg_info, bool value) { stage_modern* stg = task_stage_modern_get_stage(*stg_info); if (stg) @@ -205,7 +242,7 @@ void task_stage_modern_set_stage_hashes(std::vector& stage_hashes, } bool task_stage_modern_unload_task() { - return task_stage_modern->SetDest(); + return task_stage_modern->DelTask(); } void task_stage_modern_free() { @@ -325,14 +362,14 @@ static void stage_detail::TaskStageModern_Load(stage_detail::TaskStageModern* a1 } static bool stage_detail::TaskStageModern_LoadTask(stage_detail::TaskStageModern* a1, const char* name) { - if (app::TaskWork::AppendTask(a1, name)) { + if (app::TaskWork::AddTask(a1, name)) { stage_detail::TaskStageModern_Reset(a1); stage_detail::TaskStageModern_TaskWindAppend(a1); return false; } else { if (!app::TaskWork::HasTaskDest(a1)) - a1->SetDest(); + a1->DelTask(); return true; } } @@ -363,7 +400,7 @@ static void stage_detail::TaskStageModern_SetStage(stage_detail::TaskStageModern } static void stage_detail::TaskStageModern_TaskWindAppend(stage_detail::TaskStageModern* a1) { - app::TaskWork::AppendTask(task_wind, a1, "CHARA WIND"); + app::TaskWork::AddTask(task_wind, a1, "CHARA WIND"); } static void stage_detail::TaskStageModern_Unload(stage_detail::TaskStageModern* a1) { @@ -409,36 +446,36 @@ static void stage_detail::TaskStageModern_Unload(stage_detail::TaskStageModern* } } -static bool object_bounding_sphere_check_visibility_shadow(obj_bounding_sphere* sphere, camera* cam, mat4* mat) { +static bool object_bounding_sphere_check_visibility_shadow(obj_bounding_sphere* sphere, mat4* view, mat4* mat) { vec3 center; mat4_mult_vec3_trans(mat, &sphere->center, ¢er); - mat4_mult_vec3_trans(&cam->view, ¢er, ¢er); + mat4_mult_vec3_trans(view, ¢er, ¢er); float_t radius = sphere->radius; - shadow* shad = rctx_ptr->draw_pass.shadow_ptr; - float_t v9 = shad->view_region * shad->range; - if ((center.x + radius) < -v9 - || (center.x - radius) > v9 - || (center.y + radius) < -v9 - || (center.y - radius) > v9 + shadow* shad = rctx_ptr->render_manager.shadow_ptr; + float_t view_region = shad->view_region * shad->range; + if ((center.x + radius) < -view_region + || (center.x - radius) > view_region + || (center.y + radius) < -view_region + || (center.y - radius) > view_region || (center.z - radius) > -shad->z_near || (center.z + radius) < -shad->z_far) return false; return true; } -static bool object_bounding_sphere_check_visibility_shadow_chara(obj_bounding_sphere* sphere, camera* cam) { +static bool object_bounding_sphere_check_visibility_shadow_chara(obj_bounding_sphere* sphere, mat4* view) { mat4 mat; - shadow* shad = rctx_ptr->draw_pass.shadow_ptr; + shadow* shad = rctx_ptr->render_manager.shadow_ptr; mat4_look_at(&shad->view_point[0], &shad->interest[0], &mat); - return object_bounding_sphere_check_visibility_shadow(sphere, cam, &mat); + return object_bounding_sphere_check_visibility_shadow(sphere, view, &mat); } -static bool object_bounding_sphere_check_visibility_shadow_stage(obj_bounding_sphere* sphere, camera* cam) { +static bool object_bounding_sphere_check_visibility_shadow_stage(obj_bounding_sphere* sphere, mat4* view) { mat4 mat; - shadow* shad = rctx_ptr->draw_pass.shadow_ptr; + shadow* shad = rctx_ptr->render_manager.shadow_ptr; mat4_look_at(&shad->view_point[1], &shad->interest[1], &mat); - return object_bounding_sphere_check_visibility_shadow(sphere, cam, &mat); + return object_bounding_sphere_check_visibility_shadow(sphere, view, &mat); } static bool stage_modern_ctrl(stage_modern* s, void* data, object_database* obj_db, texture_database* tex_db) { @@ -450,14 +487,6 @@ static bool stage_modern_ctrl(stage_modern* s, void* data, object_database* obj_ stage_modern_free(s); return true; } - - for (auth_3d_id& i : s->auth_3d_ids) { - i.set_repeat(true); - i.set_paused(false); - i.set_enable(true); - i.set_visibility(s->effect_display); - i.set_frame_rate(0); - } return false; } @@ -465,36 +494,32 @@ static void stage_modern_disp(stage_modern* s) { if (s->state != 6 || !s->stage_display) return; - object_data* object_data = &rctx_ptr->object_data; + mdl::DispManager& disp_manager = rctx_ptr->disp_manager; mat4 mat; mat4_rotate_y(s->rot_y, &mat); if (s->stage_data->object_ground.not_null_modern() && s->ground) - draw_task_add_draw_object_by_object_info(rctx_ptr, - &mat, s->stage_data->object_ground, 0, 0, 0, 0, 0, 0); + disp_manager.entry_obj_by_object_info(&mat, s->stage_data->object_ground); if (s->stage_data->object_reflect.not_null_modern()) { - object_data->set_draw_task_flags( + disp_manager.set_draw_task_flags( (draw_task_flags)(DRAW_TASK_NO_TRANSLUCENCY | DRAW_TASK_REFRACT)); - draw_task_add_draw_object_by_object_info(rctx_ptr, - &mat, s->stage_data->object_reflect, 0, 0, 0, 0, 0, 0); - object_data->set_draw_task_flags(); + disp_manager.entry_obj_by_object_info(&mat, s->stage_data->object_reflect); + disp_manager.set_draw_task_flags(); } if (s->stage_data->object_refract.not_null_modern()) { - object_data->set_draw_task_flags( + disp_manager.set_draw_task_flags( (draw_task_flags)(DRAW_TASK_NO_TRANSLUCENCY | DRAW_TASK_REFRACT)); - draw_task_add_draw_object_by_object_info(rctx_ptr, - &mat, s->stage_data->object_refract, 0, 0, 0, 0, 0, 0); - object_data->set_draw_task_flags(); + disp_manager.entry_obj_by_object_info(&mat, s->stage_data->object_refract); + disp_manager.set_draw_task_flags(); } if (s->stage_data->object_sky.not_null_modern() && s->sky) { mat4 t = s->mat; mat4_mult(&t, &mat, &t); - draw_task_add_draw_object_by_object_info(rctx_ptr, - &t, s->stage_data->object_sky, 0, 0, 0, 0, 0, 0); + disp_manager.entry_obj_by_object_info(&t, s->stage_data->object_sky); } } @@ -509,21 +534,21 @@ static void stage_modern_disp_shadow(stage_modern* s) { } static void stage_modern_disp_shadow_object(object_info object, mat4* mat) { - object_data* object_data = &rctx_ptr->object_data; + mdl::DispManager& disp_manager = rctx_ptr->disp_manager; for (int32_t i = SHADOW_CHARA; i < SHADOW_MAX; i++) { - object_data->set_shadow_type((shadow_type_enum)i); - object_data->set_object_bounding_sphere_check_func(i == SHADOW_CHARA + disp_manager.set_shadow_type((shadow_type_enum)i); + disp_manager.set_culling_finc(i == SHADOW_CHARA ? object_bounding_sphere_check_visibility_shadow_chara : object_bounding_sphere_check_visibility_shadow_stage); - object_data->set_draw_task_flags( + disp_manager.set_draw_task_flags( (draw_task_flags)(DRAW_TASK_NO_TRANSLUCENCY | DRAW_TASK_SHADOW_OBJECT)); - draw_task_add_draw_object_by_object_info(rctx_ptr, mat, object, 0, 0, 0, 0, 0, 0); + disp_manager.entry_obj_by_object_info(mat, object); } - object_data->set_draw_task_flags(); - object_data->set_object_bounding_sphere_check_func(); - object_data->set_shadow_type(); + disp_manager.set_draw_task_flags(); + disp_manager.set_culling_finc(); + disp_manager.set_shadow_type(); } static void stage_modern_free(stage_modern* s) { @@ -536,7 +561,7 @@ static void stage_modern_free(stage_modern* s) { if (s->state < 7 || s->state > 9) return; - draw_pass* draw_pass = &rctx_ptr->draw_pass; + rndr::RenderManager* render_manager = &rctx_ptr->render_manager; if (s->stage_data->render_texture != -1 && s->stage_data->render_texture != hash_murmurhash_empty) rctx_ptr->post_process.render_texture_free( texture_storage_get_texture(s->stage_data->render_texture), 0); @@ -545,14 +570,14 @@ static void stage_modern_free(stage_modern* s) { rctx_ptr->post_process.movie_texture_free( texture_storage_get_texture(s->stage_data->movie_texture)); - draw_pass->set_shadow_true(); + render_manager->set_shadow_true(); //rctx_ptr->post_process.field_14 = 0; //rctx_ptr->post_process.field_ED8 = 1; - npr_spec_color.w = 1.0f; - draw_pass->field_31D = 0; - draw_pass->field_31E = 0; - draw_pass->field_31F = false; - draw_pass->field_320 = 0; + npr_cloth_spec_color.w = 1.0f; + render_manager->field_31D = false; + render_manager->field_31E = false; + render_manager->field_31F = false; + render_manager->field_320 = false; light_chara_ambient = false; if (s->auth_3d_loaded) { @@ -616,41 +641,35 @@ static void stage_modern_reset(stage_modern* s) { s->mat = mat4_identity; s->rot_y = 0.0; s->obj_set_hash = hash_murmurhash_empty; - - for (auth_3d_id& i : s->auth_3d_ids) - i.unload_id(rctx_ptr); - s->auth_3d_ids.clear(); - s->auth_3d_ids.shrink_to_fit(); - s->effect_display = true; } static void stage_modern_set(stage_modern* s, stage_modern* other) { if (other && s == other) return; - draw_pass* draw_pass = &rctx_ptr->draw_pass; + rndr::RenderManager* render_manager = &rctx_ptr->render_manager; if (s) { //flt_14CC925C0 = 0.0; //flt_140CB3704 = -1001.0; - draw_pass->set_enable(DRAW_PASS_REFLECT, false); - draw_pass->set_reflect(false); - draw_pass->set_enable(DRAW_PASS_REFRACT, false); - draw_pass->set_refract(true); - draw_pass->field_31D = false; - draw_pass->field_31E = false; - draw_pass->field_31F = false; - draw_pass->field_320 = false; - draw_pass->set_shadow_true(); + render_manager->set_pass_sw(rndr::RND_PASSID_REFLECT, false); + render_manager->set_reflect(false); + render_manager->set_pass_sw(rndr::RND_PASSID_REFRACT, false); + render_manager->set_refract(true); + render_manager->field_31D = false; + render_manager->field_31E = false; + render_manager->field_31F = false; + render_manager->field_320 = false; + render_manager->set_shadow_true(); //rctx_ptr->post_process.field_14 = 0; //rctx_ptr->post_process.field_ED8 = 1; - npr_spec_color.w = 1.0f; - draw_pass->set_npr_param(0); + npr_cloth_spec_color.w = 1.0f; + render_manager->set_npr_param(0); light_chara_ambient = false; } if (other) { //if (stru_14CC92630.pv_id == 421) - //draw_pass->field_31F = true; + //render_manager->field_31F = true; //sub_14064DC10(); task_effect_parent_set_current_stage_hash(other->hash); } @@ -701,58 +720,3 @@ static void stage_modern_set_by_stage_hash(stage_modern* s, int32_t stage_hash, if (set_name[0]) s->obj_set_hash = hash_utf8_murmurhash(set_name); } - -stage_modern::stage_modern() : counter(), state(), stage_data(), stage_display(), -ground(), sky(), auth_3d_loaded(), mat(), rot_y(), effect_display() { - hash = hash_murmurhash_empty; - obj_set_hash = hash_murmurhash_empty; -} - -stage_modern::~stage_modern() { - -} - -stage_detail::TaskStageModern::TaskStageModern() : state(), current_stage(), stage_display(), field_FB1(), -field_FB2(), field_FB3(), field_FB4(), mat(), field_FF8(), data(), obj_db(), tex_db(), stage_data() { - -} - -stage_detail::TaskStageModern:: ~TaskStageModern() { - -} - -bool stage_detail::TaskStageModern::Init() { - return true; -} - -bool stage_detail::TaskStageModern::Ctrl() { - stage_detail::TaskStageModern_CtrlInner(this); - - for (int32_t i = 0; i < TASK_STAGE_STAGE_COUNT; i++) - if (stages[i].hash != -1 && stages[i].hash != hash_murmurhash_empty - && stage_modern_ctrl(&stages[i], data, obj_db, tex_db)) - break; - return false; -} - -bool stage_detail::TaskStageModern::Dest() { - stage_detail::TaskStageModern_Unload(this); - if (state) - return false; - stage_detail::TaskStageModern_Reset(this); - return true; -} - -void stage_detail::TaskStageModern::Disp() { - if (state != 6 || !stage_display) - return; - - stage_modern* s = stage_detail::TaskStageModern_GetCurrentStage(this); - if (s) - stage_modern_disp(s); -} - -task_stage_modern_info::task_stage_modern_info() { - load_index = -1; - load_counter = 0; -} diff --git a/src/CRE/stage_modern.hpp b/src/CRE/stage_modern.hpp index f904aab5..9042b391 100644 --- a/src/CRE/stage_modern.hpp +++ b/src/CRE/stage_modern.hpp @@ -24,12 +24,7 @@ struct stage_modern { float_t rot_y; uint32_t obj_set_hash; - // Temp - std::vector auth_3d_ids; - bool effect_display; - stage_modern(); - ~stage_modern(); }; #define TASK_STAGE_STAGE_COUNT 37 @@ -76,7 +71,6 @@ struct task_stage_modern_info { extern void task_stage_modern_init(); extern bool task_stage_modern_check_not_loaded(); extern bool task_stage_modern_check_task_ready(); -extern void task_stage_modern_current_set_effect_display(bool value); extern void task_stage_modern_current_set_ground(bool value); extern void task_stage_modern_current_set_sky(bool value); extern void task_stage_modern_current_set_stage_display(bool value, bool effect_enable); @@ -91,7 +85,6 @@ extern bool task_stage_modern_has_stage_info(task_stage_modern_info* stg_info); extern bool task_stage_modern_load_task(const char* name); extern void task_stage_modern_set_data(void* data, object_database* obj_db, texture_database* tex_db, stage_database* stage_data); -extern void task_stage_modern_set_effect_display(task_stage_modern_info* stg_info, bool value); extern void task_stage_modern_set_ground(task_stage_modern_info* stg_info, bool value); extern void task_stage_modern_set_mat(mat4* mat); extern void task_stage_modern_set_sky(task_stage_modern_info* stg_info, bool value); diff --git a/src/CRE/static_var.cpp b/src/CRE/static_var.cpp index e90ef439..5690a15a 100644 --- a/src/CRE/static_var.cpp +++ b/src/CRE/static_var.cpp @@ -4,7 +4,7 @@ */ #include "static_var.hpp" -#include "timer.hpp" +#include "../KKdLib/timer.hpp" extern timer* render_timer; diff --git a/src/CRE/static_var.hpp b/src/CRE/static_var.hpp index 96479da6..cf797b79 100644 --- a/src/CRE/static_var.hpp +++ b/src/CRE/static_var.hpp @@ -14,85 +14,87 @@ #include enum draw_pass_3d_type { - DRAW_PASS_3D_OPAQUE = 0, - DRAW_PASS_3D_TRANSLUCENT = 1, - DRAW_PASS_3D_TRANSPARENT = 2, - DRAW_PASS_3D_MAX = 3, + DRAW_PASS_3D_OPAQUE = 0, + DRAW_PASS_3D_TRANSLUCENT, + DRAW_PASS_3D_TRANSPARENT, + DRAW_PASS_3D_MAX, }; enum uniform_name { - U_NONE = 0x00, - U_ALPHA_MASK = 0x01, - U_ALPHA_TEST = 0x02, - U_ANISO = 0x03, - U_AET_BACK = 0x04, - U_TEXTURE_BLEND = 0x05, - U_CHARA_COLOR = 0x06, - U_CLIP_PLANE = 0x07, - U08 = 0x08, - U_DEPTH_PEEL = 0x09, - U0A = 0x0A, - U0B = 0x0B, - U_ALPHA_BLEND = 0x0C, - U_RIPPLE_EMIT = 0x0D, - U_ESM_FILTER = 0x0E, - U_EXPOSURE = 0x0F, - U_SCENE_FADE = 0x10, - U_FADE = 0x11, - U12 = 0x12, - U_FLARE = 0x13, - U_FOG_HEIGHT = 0x14, - U_FOG = 0x15, - U16 = 0x16, - U_GAUSS = 0x17, - U18 = 0x18, - U_IMAGE_FILTER = 0x19, - U_INSTANCE = 0x1A, - U_TONE_CURVE = 0x1B, - U_LIGHT_PROJ = 0x1C, - U_MAGNIFY = 0x1D, - U_MEMBRANE = 0x1E, - U_MLAA = 0x1F, - U_MLAA_SEARCH = 0x20, - U_MORPH_COLOR = 0x21, - U_MORPH = 0x22, - U_MOVIE = 0x23, - U24 = 0x24, - U25 = 0x25, - U26 = 0x26, - U_NPR = 0x27, - U_LIGHT_1 = 0x28, - U_REFLECT = 0x29, - U_REDUCE = 0x2A, - U_SELF_SHADOW = 0x2B, - U_SHADOW = 0x2C, - U2D = 0x2D, - U2E = 0x2E, - U_SHOW_VECTOR = 0x2F, - U_BONE_MAT = 0x30, - U_SNOW_PARTICLE = 0x31, - U_SPECULAR_IBL = 0x32, - U_SPRITE_BLEND = 0x33, - U_TEX_0_TYPE = 0x34, - U_TEX_1_TYPE = 0x35, - U_SSS_FILTER = 0x36, - U37 = 0x37, - U_STAR = 0x38, - U_TEXTURE_COUNT = 0x39, - U_ENV_MAP = 0x3A, - U_RIPPLE = 0x3B, - U_TRANSLUCENCY = 0x3C, - U_NORMAL = 0x3D, - U_TRANSPARENCY = 0x3E, - U_WATER_REFLECT = 0x3F, - U40 = 0x40, - U41 = 0x41, - U_LIGHT_0 = 0x42, - U_SPECULAR = 0x43, - U_TONE_MAP = 0x44, - U45 = 0x45, - U_MAX = 0x46, - U_INVALID = 0xFFFFFFFF, + U_NONE = 0, + U_ALPHA_MASK, + U_ALPHA_TEST, + U_ANISO, + U_AET_BACK, + U_TEXTURE_BLEND, + U_CHARA_COLOR, + U_CLIP_PLANE, + U08, + U_DEPTH_PEEL, + U0A, + U0B, + U_ALPHA_BLEND, + U_RIPPLE_EMIT, + U_ESM_FILTER, + U_EXPOSURE, + U_SCENE_FADE, + U_FADE, + U12, + U_FLARE, + U_FOG_HEIGHT, + U_FOG, + U16, + U_GAUSS, + U18, + U_IMAGE_FILTER, + U_INSTANCE, + U_TONE_CURVE, + U_LIGHT_PROJ, + U_MAGNIFY, + U_MEMBRANE, + U_MLAA, + U_MLAA_SEARCH, + U_MORPH_COLOR, + U_MORPH, + U_MOVIE, + U24, + U25, + U26, + U_NPR, + U_LIGHT_1, + U_REFLECT, + U_REDUCE, + U_SELF_SHADOW, + U_SHADOW, + U2D, + U2E, + U_SHOW_VECTOR, + U_BONE_MAT, + U_SNOW_PARTICLE, + U_SPECULAR_IBL, + U_COMBINER, + U_TEX_0_TYPE, + U_TEX_1_TYPE, + U_SSS_FILTER, + U37, + U_STAR, + U_TEXTURE_COUNT, + U_ENV_MAP, + U_RIPPLE, + U_TRANSLUCENCY, + U_NORMAL, + U_TRANSPARENCY, + U_WATER_REFLECT, + U40, + U41, + U_LIGHT_0, + U_SPECULAR, + U_TONE_MAP, + U45, + U_DOF, // Added + U_DOF_STAGE, // Added + U_MAX, + U_INVALID = -1, }; extern int32_t sv_max_texture_buffer_size; diff --git a/src/CRE/task.cpp b/src/CRE/task.cpp index 5d00ec82..7e5c9e96 100644 --- a/src/CRE/task.cpp +++ b/src/CRE/task.cpp @@ -4,7 +4,7 @@ */ #include "task.hpp" -#include "time.hpp" +#include "../KKdLib/time.hpp" extern float_t get_delta_frame(); extern uint32_t get_frame_counter(); @@ -21,9 +21,9 @@ namespace app { static void Task_set_base_calc_time(Task* t, uint32_t value); static void Task_set_calc_time(Task* t); static void Task_set_disp_time(Task* t, uint32_t value); - static bool Task_sub_14019B810(Task* t, int32_t a2); - static void Task_sub_14019C480(Task* t, uint32_t a2); - static void Task_sub_14019C5A0(Task* t); + static bool Task_check_request(Task* t, Task::Request request); + static void Task_set_request(Task* t, Task::Request request); + static void Task_update_op_state(Task* t); TaskWork* task_work; @@ -58,11 +58,11 @@ namespace app { Task::Task() { priority = 1; parent_task = 0; - field_18 = Task::Enum::None; - field_1C = 0; - field_20 = 0; - field_24 = Task::Enum::None; - field_28 = 0; + op = Task::Op::None; + state = Task::State::None; + request = Task::Request::None; + next_op = Task::Op::None; + next_state = Task::State::None; field_2C = false; is_frame_dependent = false; SetName("(unknown)"); @@ -99,11 +99,35 @@ namespace app { return name; } - bool Task::SetDest() { - if (!TaskWork::HasTask(this) || !Task_sub_14019B810(this, 2)) + bool Task::DelTask() { + if (!TaskWork::HasTask(this) || !Task_check_request(this, Task::Request::Dest)) return false; - Task_sub_14019C480(this, 2); + Task_set_request(this, Task::Request::Dest); + return true; + } + + bool Task::HideTask() { + if (!TaskWork::HasTask(this) || !Task_check_request(this, Task::Request::Hide)) + return false; + + Task_set_request(this, Task::Request::Hide); + return true; + } + + bool Task::RunTask() { + if (!TaskWork::HasTask(this) || !Task_check_request(this, Task::Request::Run)) + return false; + + Task_set_request(this, Task::Request::Run); + return true; + } + + bool Task::SuspendTask() { + if (!TaskWork::HasTask(this) || !Task_check_request(this, Task::Request::Suspend)) + return false; + + Task_set_request(this, Task::Request::Suspend); return true; } @@ -123,29 +147,13 @@ namespace app { this->priority = priority; } - bool Task::sub_14019C3B0() { - if (!TaskWork::HasTask(this) || !Task_sub_14019B810(this, 5)) - return false; - - Task_sub_14019C480(this, 5); - return true; - } - - bool Task::sub_14019C540() { - if (!TaskWork::HasTask(this) || !Task_sub_14019B810(this, 4)) - return false; - - Task_sub_14019C480(this, 4); - return true; - } - TaskWork::TaskWork() : current(), disp() { } TaskWork::~TaskWork() { for (Task* i : tasks) - i->SetDest(); + i->DelTask(); while (tasks.size()) { Ctrl(); @@ -153,24 +161,24 @@ namespace app { } } - bool TaskWork::AppendTask(Task* t, const char* name, int32_t priority) { - return TaskWork::AppendTask(t, task_work->current, name, priority); + bool TaskWork::AddTask(Task* t, const char* name, int32_t priority) { + return TaskWork::AddTask(t, task_work->current, name, priority); } - bool TaskWork::AppendTask(Task* t, Task* parent_task, const char* name, int32_t priority) { - Task_sub_14019C480(t, 0); - if (TaskWork::HasTask(t) || !Task_sub_14019B810(t, 1)) + bool TaskWork::AddTask(Task* t, Task* parent_task, const char* name, int32_t priority) { + Task_set_request(t, Task::Request::None); + if (TaskWork::HasTask(t) || !Task_check_request(t, Task::Request::Init)) return false; t->SetPriority(priority); t->parent_task = parent_task; - t->field_18 = Task::Enum::None; - t->field_1C = 0; - t->field_24 = Task::Enum::None; - t->field_28 = 0; + t->op = Task::Op::None; + t->state = Task::State::None; + t->next_op = Task::Op::None; + t->next_state = Task::State::None; t->SetName(name); - Task_sub_14019C480(t, 1); - Task_sub_14019C5A0(t); + Task_set_request(t, Task::Request::Init); + Task_update_op_state(t); task_work->tasks.push_back(t); return true; } @@ -183,16 +191,16 @@ namespace app { } bool TaskWork::CheckTaskCtrl(Task* t) { - return app::TaskWork::HasTask(t) && t->field_1C == 1 && t->field_18 == Task::Enum::Ctrl; + return app::TaskWork::HasTask(t) && t->state == Task::State::Running && t->op == Task::Op::Ctrl; } bool TaskWork::CheckTaskReady(Task* t) { - return TaskWork::HasTask(t) && (t->field_18 == Task::Enum::None || t->field_1C); + return TaskWork::HasTask(t) && (t->op == Task::Op::None || t->state != Task::State::None); } void TaskWork::Ctrl() { for (Task*& i : task_work->tasks) { - Task_sub_14019C5A0(i); + Task_update_op_state(i); Task_set_base_calc_time(i, 0); } @@ -231,7 +239,7 @@ namespace app { void TaskWork::Dest() { for (Task*& i : task_work->tasks) - i->SetDest(); + i->DelTask(); } void TaskWork::Disp() { @@ -260,7 +268,9 @@ namespace app { } bool TaskWork::HasTask(Task* t) { - std::list* tasks = &task_work->tasks; + if (!task_work->tasks.size()) + return false; + for (Task*& i : task_work->tasks) if (i == t) return true; @@ -269,21 +279,21 @@ namespace app { bool TaskWork::HasTaskInit(Task* t) { if (TaskWork::HasTask(t)) - return t->field_18 == Task::Enum::Init; + return t->op == Task::Op::Init; else return false; } bool TaskWork::HasTaskCtrl(Task* t) { if (TaskWork::HasTask(t)) - return t->field_18 == Task::Enum::Ctrl; + return t->op == Task::Op::Ctrl; else return false; } bool TaskWork::HasTaskDest(Task* t) { if (TaskWork::HasTask(t)) - return t->field_18 == Task::Enum::Dest; + return t->op == Task::Op::Dest; else return false; } @@ -308,29 +318,29 @@ namespace app { } static void Task_do_basic(Task* t) { - if ((t->field_1C == 1 || t->field_1C == 2) - && t->field_18 != Task::Enum::Init && t->field_18 != Task::Enum::Dest) + if ((t->state == Task::State::Running || t->state == Task::State::Suspended) + && t->op != Task::Op::Init && t->op != Task::Op::Dest) t->Basic(); } static void Task_do_ctrl(Task* t) { - if (t->field_1C != 1) + if (t->state != Task::State::Running) return; - if (t->field_18 == Task::Enum::Init && t->Init()) { - t->field_24 = Task::Enum::Ctrl; - t->field_18 = Task::Enum::Ctrl; + if (t->op == Task::Op::Init && t->Init()) { + t->next_op = Task::Op::Ctrl; + t->op = Task::Op::Ctrl; } - if (t->field_18 == Task::Enum::Ctrl && t->Ctrl()) { - t->field_24 = Task::Enum::Dest; - t->field_18 = Task::Enum::Dest; + if (t->op == Task::Op::Ctrl && t->Ctrl()) { + t->next_op = Task::Op::Dest; + t->op = Task::Op::Dest; } - if (t->field_18 == Task::Enum::Dest && t->Dest()) { - t->field_28 = 0; - t->field_24 = Task::Enum::Max; - t->field_20 = 0; + if (t->op == Task::Op::Dest && t->Dest()) { + t->next_state = Task::State::None; + t->next_op = Task::Op::Max; + t->request = Task::Request::None; } } @@ -357,8 +367,8 @@ namespace app { } static void Task_do_disp(Task* t) { - if ((t->field_1C == 1 || t->field_1C == 2) - && t->field_18 != Task::Enum::Init && t->field_18 != Task::Enum::Dest) + if ((t->state == Task::State::Running || t->state == Task::State::Suspended) + && t->op != Task::Op::Init && t->op != Task::Op::Dest) t->Disp(); } @@ -382,57 +392,59 @@ namespace app { t->disp_time = max_def(t->disp_time, t->disp_time_max); } - static bool Task_sub_14019B810(Task* t, int32_t a2) { + static bool Task_check_request(Task* t, Task::Request request) { if (task_work->disp) return false; - if (a2 == 1) + switch (request) { + case Task::Request::Init: return true; - else if (a2 == 2) - return t->field_1C != 0; - else if (a2 == 3) - return t->field_1C == 1 || t->field_1C == 3; - else if (a2 == 4) - return t->field_1C == 1 || t->field_1C == 2; - else if (a2 == 5) - return t->field_1C == 2 || t->field_1C == 3; + case Task::Request::Dest: + return t->state != Task::State::None; + case Task::Request::Suspend: + return t->state == Task::State::Running || t->state == Task::State::Hidden; + case Task::Request::Hide: + return t->state == Task::State::Running || t->state == Task::State::Suspended; + case Task::Request::Run: + return t->state == Task::State::Suspended || t->state == Task::State::Hidden; + } return false; } - static void Task_sub_14019C480(Task* t, uint32_t a2) { - if (a2 > 1) + static void Task_set_request(Task* t, Task::Request request) { + if (request > Task::Request::Init) for (Task*& i : task_work->tasks) if (i->parent_task == t) - Task_sub_14019C480(i, a2); - t->field_20 = a2; + Task_set_request(i, request); + t->request = request; } - static void Task_sub_14019C5A0(Task* t) { - if (t->field_18 != Task::Enum::Init && t->field_18 != Task::Enum::Dest - && Task_sub_14019B810(t, t->field_20)) { - switch (t->field_20) { - case 1: - t->field_24 = Task::Enum::Init; - t->field_28 = 1; + static void Task_update_op_state(Task* t) { + if (t->op != Task::Op::Init && t->op != Task::Op::Dest + && Task_check_request(t, t->request)) { + switch (t->request) { + case Task::Request::Init: + t->next_op = Task::Op::Init; + t->next_state = Task::State::Running; break; - case 2: - t->field_24 = Task::Enum::Dest; - t->field_28 = 1; + case Task::Request::Dest: + t->next_op = Task::Op::Dest; + t->next_state = Task::State::Running; break; - case 3: - t->field_28 = 2; + case Task::Request::Suspend: + t->next_state = Task::State::Suspended; break; - case 4: - t->field_28 = 3; + case Task::Request::Hide: + t->next_state = Task::State::Hidden; break; - case 5: - t->field_28 = 1; + case Task::Request::Run: + t->next_state = Task::State::Running; break; } - t->field_20 = 0; + t->request = Task::Request::None; } - t->field_18 = t->field_24; - t->field_1C = t->field_28; + t->op = t->next_op; + t->state = t->next_state; } } diff --git a/src/CRE/task.hpp b/src/CRE/task.hpp index 17580795..c0e07cb0 100644 --- a/src/CRE/task.hpp +++ b/src/CRE/task.hpp @@ -23,21 +23,37 @@ namespace app { class Task : public TaskInterface { public: - enum class Enum { - None = 0x00, - Init = 0x01, - Ctrl = 0x02, - Dest = 0x03, - Max = 0x04, + enum class Op { + None = 0, + Init, + Ctrl, + Dest, + Max, + }; + + enum class Request { + None = 0, + Init, + Dest, + Suspend, + Hide, + Run, + }; + + enum class State { + None = 0, + Running, + Suspended, + Hidden, }; int32_t priority; Task* parent_task; - Enum field_18; - uint32_t field_1C; - uint32_t field_20; - Enum field_24; - uint32_t field_28; + Op op; + State state; + Request request; + Op next_op; + State next_state; bool field_2C; bool is_frame_dependent; char name[32]; @@ -58,12 +74,13 @@ namespace app { uint32_t GetDispTimeMax(); char* GetName(); - bool SetDest(); + bool DelTask(); + bool HideTask(); + bool RunTask(); + bool SuspendTask(); + void SetName(const char* name); void SetPriority(int32_t priority); - - bool sub_14019C3B0(); - bool sub_14019C540(); }; struct TaskWork; @@ -78,9 +95,9 @@ namespace app { TaskWork(); ~TaskWork(); - static bool AppendTask(Task* t, + static bool AddTask(Task* t, const char* name = "(unknown)", int32_t priority = 1); - static bool AppendTask(Task* t, Task* parent_task, + static bool AddTask(Task* t, Task* parent_task, const char* name = "(unknown)", int32_t priority = 1); static void Basic(); static bool CheckTaskCtrl(Task* t); diff --git a/src/CRE/task_effect.cpp b/src/CRE/task_effect.cpp index aea34add..a9433546 100644 --- a/src/CRE/task_effect.cpp +++ b/src/CRE/task_effect.cpp @@ -4,10 +4,11 @@ */ #include "task_effect.hpp" +#include "../KKdLib/prj/algorithm.hpp" #include "../KKdLib/hash.hpp" +#include "GL/uniform_buffer.hpp" #include "auth_3d.hpp" #include "data.hpp" -#include "draw_task.hpp" #include "random.hpp" #include "render_context.hpp" #include "shader_ft.hpp" @@ -31,6 +32,16 @@ struct leaf_particle_data { 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; @@ -73,6 +84,19 @@ struct rain_particle_data { rain_particle_data(); }; +struct rain_particle_scene_shader_data { + vec4 g_view[4]; + vec4 g_proj[4]; + vec4 g_range_scale; + vec4 g_range_offset; +}; + +struct rain_particle_batch_shader_data { + vec4 g_pos_offset; + vec4 g_tangent; + vec4 g_color; +}; + struct struc_608 { const stage_effects* stage_effects; const stage_effects_modern* stage_effects_modern; @@ -141,9 +165,6 @@ static void leaf_particle_ctrl(); static int32_t leaf_particle_disp(); static void leaf_particle_free(); -static void litproj_textures_init(); -static void litproj_textures_free(); - static void particle_init(vec3* offset); static void particle_ctrl(); static int32_t particle_disp(particle_vertex_data* vtx_data, particle_data* data, int32_t count); @@ -194,6 +215,7 @@ static leaf_particle_data* leaf_ptcl_data; static GLuint leaf_ptcl_vao; static GLuint leaf_ptcl_vbo; static GLuint leaf_ptcl_ebo; +static GL::UniformBuffer leaf_particle_scene_ubo; static const size_t leaf_ptcl_count = 0x800; static bool light_proj_enable; @@ -216,7 +238,8 @@ static uint32_t rain_particle_tex_id; static rain_particle_data rain_ptcl_data[8]; static GLuint rain_ptcl_vao; static GLuint rain_ptcl_vbo; -static GLuint rain_ptcl_ebo; +static GL::UniformBuffer rain_particle_scene_ubo; +static GL::UniformBuffer rain_particle_batch_ubo; static const size_t rain_ptcl_count = 0x8000; static TaskEffect** task_effect_data_array[] = { @@ -268,6 +291,8 @@ static const char* task_effect_name_array[] = { }; extern render_context* rctx_ptr; +extern int32_t width; +extern int32_t height; TaskEffect::TaskEffect() { @@ -382,7 +407,7 @@ TaskEffectAuth3D::~TaskEffectAuth3D() { bool TaskEffectAuth3D::Init() { if (!task_auth_3d_check_task_ready()) { - SetDest(); + DelTask(); return true; } @@ -450,33 +475,28 @@ void TaskEffectAuth3D::SetStageHashes(std::vector& stage_hashes, void* this->stage_hashes.assign(stage_hashes.begin(), stage_hashes.end()); stage_indices.clear(); for (uint32_t i : this->stage_hashes) { - stage_data_modern* stg_data = stage_data->get_stage_data_modern(i); + 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) { - auth_3d_id id = {}; + if (stage.count != stage.max_count) for (uint32_t j : stg_data->auth_3d_ids) { - id = {}; if (stage.count == stage.max_count) break; - id = auth_3d_data_load_hash(j, data, obj_db, tex_db); - if (id.check_not_empty()) + auth_3d_id id = auth_3d_data_load_hash(j, data, obj_db, tex_db); + if (!id.check_not_empty()) break; - } - if (id.not_null()) { 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); } @@ -490,33 +510,28 @@ void TaskEffectAuth3D::SetStageIndices(std::vector& stage_indices) { 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; - stage_data* stage_data = aft_stage_data->get_stage_data(i); + 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) { - auth_3d_id id = -1; + if (stage.count != stage.max_count) for (int32_t j : stage_data->auth_3d_ids) { - id = -1; if (stage.count == stage.max_count) break; - id = auth_3d_data_load_uid(j, aft_auth_3d_db); - if (id.check_not_empty()) + auth_3d_id id = auth_3d_data_load_uid(j, aft_auth_3d_db); + if (!id.check_not_empty()) break; - } - if (id.not_null()) { 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); } @@ -734,7 +749,7 @@ struc_573::struc_573() : chara_index(), bone_index() { TaskEffectFogRing::Data::Data() : enable(), delta_frame(), field_8(), ring_size(), tex_id(), ptcl_size(), max_ptcls(), num_ptcls(), density(), density_offset(), ptcl_data(), num_vtx(), -field_124(), field_2B8(), field_2B9(), disp(), frame_rate_control(), vao(), vbo(), ebo() { +field_124(), field_2B8(), field_2B9(), disp(), frame_rate_control(), vao(), vbo() { current_stage_index = -1; } @@ -830,7 +845,7 @@ void TaskEffectFogRing::Data::CalcVert() { } vec4 color = this->color; - for (int32_t i = num_ptcls; i > 0; i--, ptcl_data++, ptcl_vtx_data += 4) { + for (int32_t i = num_ptcls; i > 0; i--, ptcl_data++, ptcl_vtx_data += 6) { float_t size = ptcl_data->size * (float_t)((1.0 / 128.0 + 1.0 / 256.0) / 2.0); color.w = ptcl_data->density * density; @@ -841,10 +856,14 @@ void TaskEffectFogRing::Data::CalcVert() { ptcl_vtx_data[0].color = color; ptcl_vtx_data[1].position = position + vec2(-size, -size); ptcl_vtx_data[1].color = color; - ptcl_vtx_data[2].position = position + vec2( size, -size); + ptcl_vtx_data[2].position = position + vec2( size, size); ptcl_vtx_data[2].color = color; - ptcl_vtx_data[3].position = position + vec2( size, size); + ptcl_vtx_data[3].position = position + vec2( size, -size); ptcl_vtx_data[3].color = color; + ptcl_vtx_data[4].position = position + vec2( size, size); + ptcl_vtx_data[4].color = color; + ptcl_vtx_data[5].position = position + vec2(-size, -size); + ptcl_vtx_data[5].color = color; } if (GLAD_GL_VERSION_4_5) @@ -854,7 +873,7 @@ void TaskEffectFogRing::Data::CalcVert() { gl_state_bind_array_buffer(0); } - num_vtx = (int32_t)(num_ptcls * 4LL); + num_vtx = (int32_t)(num_ptcls * 6LL); } void TaskEffectFogRing::Data::Ctrl() { @@ -892,20 +911,15 @@ void TaskEffectFogRing::Data::Dest() { vbo = 0; } - if (ebo) { - glDeleteBuffers(1, &ebo); - ebo = 0; - } - - rctx_ptr->draw_pass.set_enable(DRAW_PASS_PRE_PROCESS, false); - rctx_ptr->draw_pass.clear_preprocess(0); + rctx_ptr->render_manager.set_pass_sw(rndr::RND_PASSID_USER, false); + rctx_ptr->render_manager.clear_user(0); rctx_ptr->draw_state.set_fog_height(false); } void TaskEffectFogRing::Data::Disp() { if (enable && disp) - draw_task_add_draw_preprocess(rctx_ptr, &mat4_identity, - (void(*)(render_context * rctx, void* data))draw_fog_particle, this, DRAW_OBJECT_PREPROCESS); + rctx_ptr->disp_manager.entry_obj_user(&mat4_identity, + (mdl::UserArgsFunc)draw_fog_particle, this, mdl::OBJ_TYPE_USER); } void TaskEffectFogRing::Data::Draw() { @@ -916,17 +930,17 @@ void TaskEffectFogRing::Data::Draw() { return; rctx->draw_state.set_fog_height(true); - render_texture& rt = rctx->draw_pass.get_render_texture(8); + render_texture& rt = rctx->render_manager.get_render_texture(8); rt.bind(); glViewport(0, 0, rt.color_texture->get_width_align_mip_level(), rt.color_texture->get_height_align_mip_level()); glClearColor(density_offset, density_offset, density_offset, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - if (draw_task_get_count(rctx, DRAW_OBJECT_PREPROCESS)) - draw_task_draw_objects_by_type(rctx, DRAW_OBJECT_PREPROCESS, 0, 0, true); + 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->draw_pass.set_effect_texture(rt.color_texture); + rctx->render_manager.set_effect_texture(rt.color_texture); glGetError(); } @@ -972,8 +986,8 @@ void TaskEffectFogRing::Data::Reset() { } void TaskEffectFogRing::Data::SetStageIndices(std::vector& stage_indices) { - rctx_ptr->draw_pass.clear_preprocess(0); - rctx_ptr->draw_pass.set_enable(DRAW_PASS_PRE_PROCESS, false); + rctx_ptr->render_manager.clear_user(0); + rctx_ptr->render_manager.set_pass_sw(rndr::RND_PASSID_USER, false); disp = false; current_stage_index = -1; this->stage_indices.clear(); @@ -1042,7 +1056,7 @@ void TaskEffectFogRing::Data::SetStageIndices(std::vector& stage_indice v3[4].position = 0.0f; } - const size_t max_ptcls_vtx_count = max_ptcls * 0x04ULL; + const size_t max_ptcls_vtx_count = (size_t)max_ptcls * 0x06; ptcl_data = force_malloc_s(fog_ring_data, max_ptcls); ptcl_data = new (ptcl_data) fog_ring_data[max_ptcls]; @@ -1068,42 +1082,17 @@ void TaskEffectFogRing::Data::SetStageIndices(std::vector& stage_indice gl_state_bind_vertex_array(vao); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, buffer_size, - (void*)offsetof(for_ring_vertex_data, position)); // Pos - glEnableVertexAttribArray(3); - glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, buffer_size, - (void*)offsetof(for_ring_vertex_data, color)); // Color0 - - size_t ebo_count = max_ptcls_vtx_count / 4 * 6; - uint32_t* ebo_data = force_malloc_s(uint32_t, ebo_count); - for (size_t i = 0, j = 0; i < max_ptcls; i += 6, j += 4) { - ebo_data[i + 0] = (uint32_t)(j + 0); - ebo_data[i + 1] = (uint32_t)(j + 1); - ebo_data[i + 2] = (uint32_t)(j + 2); - ebo_data[i + 3] = (uint32_t)(j + 0); - ebo_data[i + 4] = (uint32_t)(j + 2); - ebo_data[i + 5] = (uint32_t)(j + 3); - } - - if (!ebo) - glGenBuffers(1, &ebo); - - gl_state_bind_element_array_buffer(ebo, true); - if (GLAD_GL_VERSION_4_4) - glBufferStorage(GL_ELEMENT_ARRAY_BUFFER, - (GLsizeiptr)(sizeof(uint32_t) * ebo_count), ebo_data, 0); - else - glBufferData(GL_ELEMENT_ARRAY_BUFFER, - (GLsizeiptr)(sizeof(uint32_t) * ebo_count), ebo_data, GL_STATIC_DRAW); - free_def(ebo_data); - + (void*)offsetof(for_ring_vertex_data, position)); + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, buffer_size, + (void*)offsetof(for_ring_vertex_data, color)); gl_state_bind_vertex_array(0); gl_state_bind_array_buffer(0); - gl_state_bind_element_array_buffer(0); InitParticleData(); - rctx_ptr->draw_pass.set_enable(DRAW_PASS_PRE_PROCESS, true); - rctx_ptr->draw_pass.add_preprocess(0, TaskEffectFogRing::Data::DrawStatic, this); + rctx_ptr->render_manager.set_pass_sw(rndr::RND_PASSID_USER, true); + rctx_ptr->render_manager.add_user(0, TaskEffectFogRing::Data::DrawStatic, this); } void TaskEffectFogRing::Data::DrawStatic(void* data) { @@ -1446,7 +1435,8 @@ bool TaskEffectLitproj::Ctrl() { } bool TaskEffectLitproj::Dest() { - litproj_textures_free(); + delete rctx_ptr->litproj; + rctx_ptr->litproj = 0; stage_param_data_litproj_current = 0; stage_param_data_litproj_storage_clear(); return true; @@ -1483,8 +1473,15 @@ void TaskEffectLitproj::SetStageIndices(std::vector& stage_indices) { 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; - litproj_textures_init(); + + 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; } } @@ -1762,10 +1759,26 @@ void leaf_particle_draw() { if (!count) return; + const light_data& light_stage = rctx_ptr->light_set[LIGHT_SET_MAIN].lights[LIGHT_STAGE]; + + leaf_particle_scene_shader_data shader_data = {}; + mat4 temp; + mat4_transpose(&rctx_ptr->vp_mat, &temp); + shader_data.g_transform[0] = temp.row0; + shader_data.g_transform[1] = temp.row1; + shader_data.g_transform[2] = temp.row2; + shader_data.g_transform[3] = temp.row3; + rctx_ptr->camera->get_view_point(shader_data.g_view_pos); + shader_data.g_color = stage_param_data_leaf_current->color; + light_stage.get_diffuse(shader_data.g_light_env_stage_diffuse); + light_stage.get_specular(shader_data.g_light_env_stage_specular); + shader_data.g_lit_dir = rctx_ptr->obj_scene.g_light_chara_dir; + shader_data.g_lit_luce = rctx_ptr->obj_scene.g_light_chara_luce; + leaf_particle_scene_ubo.WriteMapMemory(shader_data); + gl_state_active_bind_texture_2d(0, tex->tex); - shaders_ft.set(SHADER_FT_LEAF_PT); - shaders_ft.local_frag_set(0, stage_param_data_leaf_current->color); - shaders_ft.state_material_set_shininess(false, 1.0f); + shaders_ft.set_opengl_shader(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); @@ -1779,39 +1792,59 @@ void rain_particle_draw() { if (!tex) return; - gl_state_enable_blend(); - gl_state_set_blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - gl_state_enable_depth_test(); - gl_state_set_depth_mask(GL_FALSE); - gl_state_active_bind_texture_2d(0, tex->tex); - shaders_ft.set(SHADER_FT_RAIN); - stage_param_rain* rain = stage_param_data_rain_current; - float_t tangent_sign = -rain->psize.x; - float_t tangent_size = -rain->psize.y * (float_t)(1.0 / 30.0); - - int32_t first = 0; - int32_t count = min_def(rain->num_rain, rain_ptcl_count) / 2; - vec3 range = rain->range; vec3 range_scale = rain->range; vec3 range_offset = rain->offset; range_offset.x -= range.x * 0.5f; range_offset.z -= range.z * 0.5f; + + rain_particle_scene_shader_data scene_shader_data = {}; + mat4 temp; + mat4_transpose(&rctx_ptr->view_mat, &temp); + scene_shader_data.g_view[0] = temp.row0; + scene_shader_data.g_view[1] = temp.row1; + scene_shader_data.g_view[2] = temp.row2; + scene_shader_data.g_view[3] = temp.row3; + mat4_transpose(&rctx_ptr->proj_mat, &temp); + scene_shader_data.g_proj[0] = temp.row0; + scene_shader_data.g_proj[1] = temp.row1; + scene_shader_data.g_proj[2] = temp.row2; + scene_shader_data.g_proj[3] = temp.row3; + scene_shader_data.g_range_scale = { range_scale.x, range_scale.y, range_scale.z, 0.0f }; + scene_shader_data.g_range_offset = { range_offset.x, range_offset.y, range_offset.z, 0.0f }; + rain_particle_scene_ubo.WriteMapMemory(scene_shader_data); + + gl_state_enable_blend(); + gl_state_set_blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl_state_enable_depth_test(); + gl_state_set_depth_mask(GL_FALSE); + gl_state_active_bind_texture_2d(0, tex->tex); + shaders_ft.set_opengl_shader(SHADER_FT_RAIN); + + float_t tangent_sign = -rain->psize.x; + float_t tangent_size = -rain->psize.y * (float_t)(1.0 / 30.0); + + int32_t first = 0; + int32_t count = min_def(rain->num_rain, rain_ptcl_count) / 2 / 4 * 6; + vec4 color = rain->color; float_t color_a = color.w; - shaders_ft.local_vert_set(1, range_offset.x, range_offset.y, range_offset.z, 0.0f); - shaders_ft.local_vert_set(2, range_scale.x, range_scale.y, range_scale.z, 0.0f); gl_state_bind_vertex_array(rain_ptcl_vao); + rain_particle_scene_ubo.Bind(0); + rain_particle_batch_ubo.Bind(1); for (int32_t i = 0; i < 8; i++, first += count) { rain_particle_data& data = rain_ptcl_data[i]; vec3 pos_offset = data.position / range; vec3 tangent = data.velocity * tangent_size; color.w = color_a * data.alpha; - shaders_ft.local_vert_set(0, pos_offset.x, pos_offset.y, pos_offset.z, 0.075f); - shaders_ft.local_vert_set(3, tangent.x, tangent.y, tangent.z, tangent_sign); - shaders_ft.local_frag_set(0, color); - shaders_ft.draw_range_elements(GL_TRIANGLES, first, count, count / 4 * 6, GL_UNSIGNED_INT, 0); + + rain_particle_batch_shader_data batch_shader_data = {}; + batch_shader_data.g_pos_offset = { pos_offset.x, pos_offset.y, pos_offset.z, 0.075f }; + batch_shader_data.g_tangent = { tangent.x, tangent.y, tangent.z, tangent_sign }; + batch_shader_data.g_color = color; + rain_particle_batch_ubo.WriteMapMemory(batch_shader_data); + shaders_ft.draw_arrays(GL_TRIANGLES, first, count); } gl_state_bind_vertex_array(0); gl_state_active_bind_texture_2d(0, 0); @@ -1853,7 +1886,7 @@ void particle_draw() { return; gl_state_bind_vertex_array(ptcl_vao); - shaders_ft.set(SHADER_FT_PARTICL); + shaders_ft.set_opengl_shader(SHADER_FT_PARTICL); shaders_ft.draw_arrays(GL_TRIANGLES, 0, count); gl_state_bind_vertex_array(0); } @@ -2048,7 +2081,7 @@ static std::string task_effect_array_get_stage_param_file_path( if (dev_ram) path.assign("dev_ram/stage_param/"); else - path.assign("./rom/stage_param/"); + path.assign("rom/stage_param/"); if (a4) return path; @@ -2206,7 +2239,7 @@ std::pair TaskEffectParent::AddTaskEffectTypePtr(Ta const char* name = 0; TaskEffect* t = task_effect_array_get(type, &name); if (t) { - app::TaskWork::AppendTask(t, name); + app::TaskWork::AddTask(t, name); return { type, t }; } } @@ -2232,7 +2265,7 @@ void TaskEffectParent::Dest() { state = 4; else if (state >= 2 && state <= 3) { for (std::pair& i : effects) - i.second->SetDest(); + i.second->DelTask(); state = 4; } } @@ -2405,7 +2438,7 @@ void TaskEffectParent::SetStageHashes(std::vector& stage_hashes) { modern = true; for (uint32_t i : stage_hashes) { - ::stage_data_modern* stage_data = this->stage_data->get_stage_data_modern(i); + const ::stage_data_modern* stage_data = this->stage_data->get_stage_data_modern(i); if (!stage_data) continue; @@ -2414,22 +2447,16 @@ void TaskEffectParent::SetStageHashes(std::vector& stage_hashes) { field_68.push_back({ i, struc_608(effects) }); - for (uint32_t j : effects->field_0) { + for (uint32_t j : effects->field_0) if (j == hash_murmurhash_empty || j == hash_murmurhash_null || j == -1) break; - - bool found = false; - for (uint32_t k : obj_set_ids) - if (k == j) { - found = true; - break; - } - - if (!found) + else obj_set_ids.push_back(j); - } } + prj::sort(obj_set_ids); + prj::unique(obj_set_ids); + for (uint32_t i : obj_set_ids) object_storage_load_set_hash(data, i); state = 1; @@ -2446,7 +2473,7 @@ void TaskEffectParent::SetStageIndices(std::vector& stage_indices) { stage_database* aft_stage_data = &aft_data->data_ft.stage_data; for (int32_t i : stage_indices) { - ::stage_data* stage_data = aft_stage_data->get_stage_data(i); + const ::stage_data* stage_data = aft_stage_data->get_stage_data(i); if (!stage_data) continue; @@ -2455,22 +2482,16 @@ void TaskEffectParent::SetStageIndices(std::vector& stage_indices) { field_68.push_back({ i, struc_608(effects) }); - for (int32_t j : effects->field_0) { + for (int32_t j : effects->field_0) if (j == -1) break; - - bool found = false; - for (uint32_t k : obj_set_ids) - if (k == j) { - found = true; - break; - } - - if (!found) + else obj_set_ids.push_back(j); - } } + prj::sort(obj_set_ids); + prj::unique(obj_set_ids); + for (uint32_t i : obj_set_ids) object_storage_load_set(aft_data, aft_obj_db, i); state = 1; @@ -2497,16 +2518,13 @@ static void draw_fog_particle(render_context* rctx, TaskEffectFogRing::Data* dat if (!data->num_vtx) return; - shaders_ft.set(SHADER_FT_FOGPTCL); + shaders_ft.set_opengl_shader(SHADER_FT_FOGPTCL); gl_state_enable_blend(); - glEnable(GL_PROGRAM_POINT_SIZE); - glPointParameteri(GL_POINT_SPRITE_COORD_ORIGIN, GL_LOWER_LEFT); texture* tex = texture_storage_get_texture(data->tex_id); if (tex) gl_state_active_bind_texture_2d(0, tex->tex); gl_state_bind_vertex_array(data->vao); - shaders_ft.draw_elements(GL_TRIANGLES, data->num_vtx / 4 * 6, GL_UNSIGNED_INT, 0); - glDisable(GL_PROGRAM_POINT_SIZE); + shaders_ft.draw_arrays(GL_TRIANGLES, 0, data->num_vtx); gl_state_disable_blend(); } @@ -2553,13 +2571,13 @@ static void leaf_particle_init(bool change_stage) { 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)); // Pos + (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)); // Normal - glEnableVertexAttribArray(8); - glVertexAttribPointer(8, 2, GL_FLOAT, GL_FALSE, buffer_size, - (void*)offsetof(leaf_particle_vertex_data, texcoord)); // TexCoords + (void*)offsetof(leaf_particle_vertex_data, normal)); size_t ebo_count = leaf_ptcl_vtx_count / 4 * 6; uint32_t* ebo_data = force_malloc_s(uint32_t, ebo_count); @@ -2587,6 +2605,7 @@ static void leaf_particle_init(bool change_stage) { 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) { @@ -2732,30 +2751,22 @@ static int32_t leaf_particle_disp() { data[0].normal = t0; if (split_tex) { - float_t u = 0.0f; - float_t v = 0.0f; - if (i & 0x01) - u = 1.0f; - if (i & 0x02) - v = 1.0f; + 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.x = u0; - vtx_data[0].texcoord.y = v0; - vtx_data[1].texcoord.x = u1; - vtx_data[1].texcoord.y = v0; - vtx_data[2].texcoord.x = u1; - vtx_data[2].texcoord.y = v1; - vtx_data[3].texcoord.x = u0; - vtx_data[3].texcoord.y = v1; + 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 = 1.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); } @@ -2807,18 +2818,8 @@ static void leaf_particle_free() { glDeleteBuffers(1, &leaf_ptcl_ebo); leaf_ptcl_ebo = 0; } -} -static void litproj_textures_init() { - litproj_shadow[0].init(2048, 512, 0, GL_R32F, GL_DEPTH_COMPONENT24); - litproj_shadow[1].init(2048, 512, 0, GL_R32F, GL_ZERO); - litproj_texture.init(1280, 720, 0, GL_RGBA8, GL_DEPTH_COMPONENT24); -} - -static void litproj_textures_free() { - litproj_shadow[0].free(); - litproj_shadow[1].free(); - litproj_texture.free(); + leaf_particle_scene_ubo.Destroy(); } static void particle_init(vec3* offset) { @@ -2862,16 +2863,16 @@ static void particle_init(vec3* offset) { gl_state_bind_vertex_array(ptcl_vao); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, buffer_size, - (void*)offsetof(particle_vertex_data, position)); // Pos + (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, 3, GL_FLOAT, GL_FALSE, buffer_size, - (void*)offsetof(particle_vertex_data, normal)); // Normal + glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, buffer_size, + (void*)offsetof(particle_vertex_data, texcoord)); glEnableVertexAttribArray(3); - glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, buffer_size, - (void*)offsetof(particle_vertex_data, color)); // Color0 - glEnableVertexAttribArray(8); - glVertexAttribPointer(8, 2, GL_FLOAT, GL_FALSE, buffer_size, - (void*)offsetof(particle_vertex_data, texcoord)); // TexCoords + 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); @@ -3083,7 +3084,7 @@ static void rain_particle_init(bool change_stage) { vec3 velocity = stage_param_data_rain_current->velocity; vec3 vel_range = stage_param_data_rain_current->vel_range; - const size_t rain_ptcl_vtx_count = rain_ptcl_count * 0x04; + const size_t rain_ptcl_vtx_count = rain_ptcl_count * 0x06; for (int32_t i = 0; i < 8; i++) { rain_particle_data& data = rain_ptcl_data[i]; @@ -3114,7 +3115,7 @@ static void rain_particle_init(bool change_stage) { glGenBuffers(1, &rain_ptcl_vbo); vec3* vtx_data = force_malloc_s(vec3, rain_ptcl_vtx_count); - for (int32_t i = 0; i < rain_ptcl_count; i++, vtx_data += 4) { + for (int32_t i = 0; i < rain_ptcl_count; i++, vtx_data += 6) { vec3 position; position.x = rand_state_array_get_float(4); position.y = rand_state_array_get_float(4); @@ -3123,6 +3124,8 @@ static void rain_particle_init(bool change_stage) { vtx_data[1] = position; vtx_data[2] = position; vtx_data[3] = position; + vtx_data[4] = position; + vtx_data[5] = position; } vtx_data -= rain_ptcl_vtx_count; @@ -3139,34 +3142,12 @@ static void rain_particle_init(bool change_stage) { gl_state_bind_vertex_array(rain_ptcl_vao); glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, buffer_size, 0); // Pos - - size_t ebo_count = rain_ptcl_vtx_count / 4 * 6; - uint32_t* ebo_data = force_malloc_s(uint32_t, ebo_count); - for (size_t i = 0, j = 0; i < ebo_count; i += 6, j += 4) { - ebo_data[i + 0] = (uint32_t)(j + 0); - ebo_data[i + 1] = (uint32_t)(j + 1); - ebo_data[i + 2] = (uint32_t)(j + 2); - ebo_data[i + 3] = (uint32_t)(j + 0); - ebo_data[i + 4] = (uint32_t)(j + 2); - ebo_data[i + 5] = (uint32_t)(j + 3); - } - - if (!rain_ptcl_ebo) - glGenBuffers(1, &rain_ptcl_ebo); - - gl_state_bind_element_array_buffer(rain_ptcl_ebo, true); - if (GLAD_GL_VERSION_4_4) - glBufferStorage(GL_ELEMENT_ARRAY_BUFFER, - (GLsizeiptr)(sizeof(uint32_t) * ebo_count), ebo_data, 0); - else - glBufferData(GL_ELEMENT_ARRAY_BUFFER, - (GLsizeiptr)(sizeof(uint32_t) * ebo_count), ebo_data, GL_STATIC_DRAW); - free_def(ebo_data); - + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, buffer_size, 0); gl_state_bind_vertex_array(0); gl_state_bind_array_buffer(0); - gl_state_bind_element_array_buffer(0); + + rain_particle_scene_ubo.Create(sizeof(rain_particle_scene_shader_data)); + rain_particle_batch_ubo.Create(sizeof(rain_particle_batch_shader_data)); } static void rain_particle_ctrl() { @@ -3202,8 +3183,6 @@ static void rain_particle_free() { rain_ptcl_vbo = 0; } - if (rain_ptcl_ebo) { - glDeleteBuffers(1, &rain_ptcl_ebo); - rain_ptcl_ebo = 0; - } + rain_particle_scene_ubo.Destroy(); + rain_particle_batch_ubo.Destroy(); } diff --git a/src/CRE/task_effect.hpp b/src/CRE/task_effect.hpp index c67c7c23..2fdd412e 100644 --- a/src/CRE/task_effect.hpp +++ b/src/CRE/task_effect.hpp @@ -234,7 +234,6 @@ public: FrameRateControl* frame_rate_control; GLuint vao; GLuint vbo; - GLuint ebo; Data(); ~Data(); diff --git a/src/CRE/texture.cpp b/src/CRE/texture.cpp index ab2c27aa..283ad198 100644 --- a/src/CRE/texture.cpp +++ b/src/CRE/texture.cpp @@ -24,18 +24,10 @@ static texture* texture_load_tex(texture_id id, GLenum target, GLenum internal_format, int32_t width, int32_t height, int32_t max_mipmap_level, void** data_ptr, bool use_high_anisotropy); static void texture_set_params(GLuint texture, GLenum target, int32_t max_mipmap_level, bool use_high_anisotropy); -static GLenum texture_txp_get_internal_format(txp* t); +static GLenum texture_txp_get_gl_internal_format(txp* t); std::vector texture_storage; -texture_id::texture_id() : id(0), index(0) { - -} - -texture_id::texture_id(uint8_t id, uint32_t index) : id(id), index(index) { - -} - texture::texture() : init_count(), flags(), width(), height(), tex(), target(), internal_format(), max_mipmap_level(), size() { @@ -62,7 +54,7 @@ texture* texture_init(texture_id id) { } void texture_apply_color_tone(texture* chg_tex, - texture* org_tex, color_tone* col_tone) { + texture* org_tex, const color_tone* col_tone) { if (!chg_tex || !org_tex || !col_tone || chg_tex->internal_format != org_tex->internal_format || chg_tex->width != org_tex->width @@ -164,7 +156,7 @@ texture* texture_txp_load(txp* t, texture_id id) { for (uint32_t j = 0; j < t->mipmaps_count; j++, k++) data_ptr[k] = t->mipmaps[k].data.data(); - GLenum internal_format = texture_txp_get_internal_format(t); + GLenum internal_format = texture_txp_get_gl_internal_format(t); int32_t width = t->mipmaps[0].width; int32_t height = t->mipmaps[0].height; int32_t max_mipmap_level = t->mipmaps_count - 1; @@ -649,7 +641,7 @@ static void texture_set_params(GLuint texture, GLenum target, int32_t max_mipmap glTexParameterf(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_anisotropy); } -static GLenum texture_txp_get_internal_format(txp* t) { +static GLenum texture_txp_get_gl_internal_format(txp* t) { if (!t || !t->mipmaps.size()) return GL_ZERO; @@ -666,17 +658,17 @@ static GLenum texture_txp_get_internal_format(txp* t) { return GL_RGB5_A1; case TXP_RGBA4: return GL_RGBA4; - case TXP_DXT1: + case TXP_BC1: return GL_COMPRESSED_RGB_S3TC_DXT1_EXT; - case TXP_DXT1a: + case TXP_BC1a: return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; - case TXP_DXT3: + case TXP_BC2: return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; - case TXP_DXT5: + case TXP_BC3: return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - case TXP_ATI1: + case TXP_BC4: return GL_COMPRESSED_RED_RGTC1_EXT; - case TXP_ATI2: + case TXP_BC5: return GL_COMPRESSED_RED_GREEN_RGTC2_EXT; case TXP_L8: return GL_LUMINANCE8; diff --git a/src/CRE/texture.hpp b/src/CRE/texture.hpp index e8332bb7..f02d09d2 100644 --- a/src/CRE/texture.hpp +++ b/src/CRE/texture.hpp @@ -18,8 +18,16 @@ struct texture_id { uint32_t index; uint8_t id; - texture_id(); - texture_id(uint8_t id, uint32_t index); + inline texture_id() { + id = 0; + index = 0; + } + + inline texture_id(uint8_t id, uint32_t index) { + this->id = id; + this->index = index; + } + }; inline bool operator >(const texture_id& left, const texture_id& right) { @@ -66,7 +74,7 @@ struct texture { extern texture* texture_init(texture_id id); extern void texture_apply_color_tone(texture* chg_tex, - texture* org_tex, color_tone* col_tone); + texture* org_tex, const color_tone* col_tone); extern texture* texture_copy(texture_id id, texture* org_tex); extern texture* texture_load_tex_2d(texture_id id, GLenum internal_format, int32_t width, int32_t height, int32_t max_mipmap_level, void** data_ptr, bool use_high_anisotropy); diff --git a/src/DivaGL/DivaGL.rc b/src/DivaGL/DivaGL.rc index 549a0eb0..8ad78045 100644 --- a/src/DivaGL/DivaGL.rc +++ b/src/DivaGL/DivaGL.rc @@ -1,46 +1,46 @@ -#define IDR_VERSION2 101 - -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 102 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1000 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif - -#define APSTUDIO_READONLY_SYMBOLS -#include "winres.h" -#undef APSTUDIO_READONLY_SYMBOLS - -VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,1,3,0 - PRODUCTVERSION 0,1,3,0 - FILEFLAGSMASK 0x3fL -#ifdef _DEBUG - FILEFLAGS 0x1L -#else - FILEFLAGS 0x0L -#endif - FILEOS 0x40004L - FILETYPE 0x0L - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "000904b0" - BEGIN - VALUE "FileDescription", "DivaGL plugin for PDAFT 7.10" - VALUE "FileVersion", "0.1.3.0" - VALUE "InternalName", "DivaGL" - VALUE "LegalCopyright", "korenkonder (C) 2020-2021" - VALUE "OriginalFilename", "DivaGL.dll" - VALUE "ProductName", "DivaGL" - VALUE "ProductVersion", "0.1.3.0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x9, 1200 - END -END +#define IDR_VERSION2 101 + +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif + +#define APSTUDIO_READONLY_SYMBOLS +#include "winres.h" +#undef APSTUDIO_READONLY_SYMBOLS + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 0,1,3,1 + PRODUCTVERSION 0,1,3,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x0L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000904b0" + BEGIN + VALUE "FileDescription", "DivaGL plugin for PDAFT 7.10" + VALUE "FileVersion", "0.1.3.1" + VALUE "InternalName", "DivaGL" + VALUE "LegalCopyright", "korenkonder (C) 2020-2023" + VALUE "OriginalFilename", "DivaGL.dll" + VALUE "ProductName", "DivaGL" + VALUE "ProductVersion", "0.1.3.1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x9, 1200 + END +END diff --git a/src/DivaGL/bone_data.cpp b/src/DivaGL/bone_data.cpp index edcb4969..54a92835 100644 --- a/src/DivaGL/bone_data.cpp +++ b/src/DivaGL/bone_data.cpp @@ -5,45 +5,10 @@ #include "bone_data.hpp" -enum HeapCMallocType { - HeapCMallocSystem = 0x00, - HeapCMallocTemp = 0x01, - HeapCMallocMode = 0x02, - HeapCMallocEvent = 0x03, - HeapCMallocDebug = 0x04, - HeapCMallocMax = 0x05, -}; - -struct skeleton_rotation_offset { - bool x; - bool y; - bool z; - vec3 rotation; -}; - -struct struc_403 { - int32_t field_0; - int32_t field_4; - int32_t field_8; -}; - -struct struc_404 { - int32_t field_0; - int32_t field_4; -}; - ExNodeBlock_vtbl* ExClothBlock_vftable = (ExNodeBlock_vtbl*)0x0000000140A82FF0; ExNodeBlock_vtbl* ExOsageBlock_vftable = (ExNodeBlock_vtbl*)0x0000000140A82F88; ExNodeBlock_vtbl* ExNodeBlock_vftable = (ExNodeBlock_vtbl*)0x0000000140A82DC8; -static const object_info object_info_null = { (uint16_t)-1, (uint16_t)-1 }; - -static bone_database_struct** bone_database_ptr = (bone_database_struct**)0x00000000140FBC1C0; -static struc_403** rob_cmn_mottbl_data_ptr = (struc_403**)0x00000001411E8258; -static void* (*HeapCMallocAllocate)(HeapCMallocType type, size_t size, const char* name) - = (void* (*)(HeapCMallocType, size_t, const char*))0x00000001403F2A00; -static void(*HeapCMallocFree)(HeapCMallocType type, void* data) - = (void(*)(HeapCMallocType, void*))0x00000001403F2960; static void* (*operator_new)(size_t) = (void* (*)(size_t))0x000000014084530C; static void(*operator_delete)(void*) = (void(*)(void*))0x0000000140845378; @@ -51,3643 +16,6 @@ static const float_t get_osage_gravity_const() { return 0.00299444468691945f; } -static void bone_node_expression_data_set_position_rotation(bone_node_expression_data* data, - float_t position_x, float_t position_y, float_t position_z, - float_t rotation_x, float_t rotation_y, float_t rotation_z) { - data->position.x = position_x; - data->position.y = position_y; - data->position.z = position_z; - data->rotation.x = rotation_x; - data->rotation.z = rotation_z; - data->rotation.y = rotation_y; - data->scale = 1.0f; - data->parent_scale = 1.0f; -} - -static void bone_node_expression_data_reset_scale(bone_node_expression_data* data) { - data->scale = 1.0f; - data->parent_scale = 1.0f; -} - -static void bone_node_data_reset_position_rotation(bone_node_expression_data* data) { - data->position = 0.0f; - data->rotation = 0.0f; - data->scale = 1.0f; - data->parent_scale = 1.0f; -} - -static bone_data* bone_data_init(bone_data* bone) { - bone->type = (bone_database_bone_type)0; - bone->has_parent = 0; - bone->motion_bone_index = (motion_bone_index)0; - bone->mirror = 0; - bone->parent = 0; - bone->flags = 0; - bone->key_set_offset = 0; - bone->key_set_count = 0; - bone->frame = 0; - - for (int32_t i = 0; i < 2; i++) - bone->base_translation[i] = 0.0f; - - bone->rotation = 0.0f; - bone->ik_target = 0.0f; - bone->trans = 0.0f; - for (int32_t i = 0; i < 2; i++) - bone->rot_mat[i] = mat4_null; - - for (int32_t i = 0; i < 2; i++) - bone->trans_prev[i] = 0.0f; - - for (int32_t i = 0; i < 2; i++) - for (int32_t j = 0; j < 3; j++) - bone->rot_mat_prev[i][j] = mat4_null; - - bone->pole_target_mat = 0; - bone->parent_mat = 0; - bone->node = 0; - bone->arm_length = 0.0f; - bone->eyes_xrot_adjust_neg = 1.0f; - bone->eyes_xrot_adjust_pos = 1.0f; - return bone; -} - -static void bone_database_bones_calculate_count(bone_database_bone* bones, size_t* object_bone_count, - size_t* motion_bone_count, size_t* total_bone_count, size_t* ik_bone_count, size_t* chain_pos) { - size_t _object_bone_count = 0; - size_t _motion_bone_count = 0; - size_t _total_bone_count = 0; - size_t _ik_bone_count = 0; - size_t _chain_pos = 0; - for (bone_database_bone* i = bones; i->type != BONE_DATABASE_BONE_END; i++) - switch (i->type) { - case BONE_DATABASE_BONE_ROTATION: - case BONE_DATABASE_BONE_TYPE_1: - case BONE_DATABASE_BONE_POSITION: - _object_bone_count++; - _motion_bone_count++; - _total_bone_count++; - _chain_pos++; - break; - case BONE_DATABASE_BONE_POSITION_ROTATION: - _object_bone_count++; - _motion_bone_count++; - _total_bone_count++; - _ik_bone_count++; - _chain_pos++; - break; - case BONE_DATABASE_BONE_HEAD_IK_ROTATION: - _object_bone_count++; - _motion_bone_count++; - _total_bone_count += 3; - _ik_bone_count++; - _chain_pos += 2; - break; - case BONE_DATABASE_BONE_ARM_IK_ROTATION: - case BONE_DATABASE_BONE_LEGS_IK_ROTATION: - _object_bone_count += 3; - _motion_bone_count++; - _total_bone_count += 4; - _ik_bone_count++; - _chain_pos += 3; - break; - } - *object_bone_count = _object_bone_count; - *motion_bone_count = _motion_bone_count; - *total_bone_count = _total_bone_count; - *ik_bone_count = _ik_bone_count; - *chain_pos = _chain_pos; -} - -static bone_database_skeleton* bone_database_get_skeleton_by_type(bone_database_skeleton_type type) { - if (type == BONE_DATABASE_SKELETON_NONE || !*bone_database_ptr - || (*bone_database_ptr)->signature != 0x9102720) - return 0; - - return (*bone_database_ptr)->skeletons[type]; -} - -static bone_database_bone* bone_database_get_bones_by_type(bone_database_skeleton_type type) { - bone_database_skeleton* skel = bone_database_get_skeleton_by_type(type); - if (!skel) - return 0; - - return skel->bones; -} - -static uint16_t* bone_database_get_skeleton_parent_indices_by_type(bone_database_skeleton_type type) { - bone_database_skeleton* skel = bone_database_get_skeleton_by_type(type); - if (!skel) - return 0; - - return skel->parent_indices; -} - -static vec3* bone_database_get_skeleton_positions_by_type(bone_database_skeleton_type type) { - bone_database_skeleton* skel = bone_database_get_skeleton_by_type(type); - if (!skel) - return 0; - - return skel->positions; -} - -static float_t bone_database_get_skeleton_heel_height(bone_database_skeleton_type a1) { - bone_database_skeleton* skel = bone_database_get_skeleton_by_type(a1); - if (skel) - return skel->heel_height[0]; - else - return 0.0f; -} - -static void rob_chara_bone_data_reserve(rob_chara_bone_data* rob_bone_data) { - if (rob_bone_data->mats.begin) - HeapCMallocFree(HeapCMallocSystem, rob_bone_data->mats.begin); - - size_t mats_size = rob_bone_data->object_bone_count; - rob_bone_data->mats.begin = (mat4*)HeapCMallocAllocate(HeapCMallocSystem, - mats_size * sizeof(mat4), "ALLOCsys"); - rob_bone_data->mats.end = rob_bone_data->mats.begin + mats_size; - rob_bone_data->mats.capacity_end = rob_bone_data->mats.begin + mats_size; - - for (mat4* i = rob_bone_data->mats.begin; i != rob_bone_data->mats.end; i++) - *i = mat4_identity; - - if (rob_bone_data->mats2.begin) - HeapCMallocFree(HeapCMallocSystem, rob_bone_data->mats2.begin); - - size_t mats2_size = rob_bone_data->total_bone_count - rob_bone_data->object_bone_count; - rob_bone_data->mats2.begin = (mat4*)HeapCMallocAllocate(HeapCMallocSystem, - mats2_size * sizeof(mat4), "ALLOCsys"); - rob_bone_data->mats2.end = rob_bone_data->mats2.begin + mats2_size; - rob_bone_data->mats2.capacity_end = rob_bone_data->mats2.begin + mats2_size; - - for (mat4* i = rob_bone_data->mats2.begin; i != rob_bone_data->mats2.end; i++) - *i = mat4_identity; - - if (rob_bone_data->nodes.begin) - HeapCMallocFree(HeapCMallocSystem, rob_bone_data->nodes.begin); - - size_t nodes_size = rob_bone_data->total_bone_count; - rob_bone_data->nodes.begin = (bone_node*)HeapCMallocAllocate(HeapCMallocSystem, - nodes_size * sizeof(bone_node), "ALLOCsys"); - rob_bone_data->nodes.end = rob_bone_data->nodes.begin + nodes_size; - rob_bone_data->nodes.capacity_end = rob_bone_data->nodes.begin + nodes_size; - - for (bone_node* i = rob_bone_data->nodes.begin; i != rob_bone_data->nodes.end; i++) { - i->name = 0; - i->mat = 0; - i->parent = 0; - i->exp_data.position = 0.0f; - i->exp_data.rotation = 0.0f; - i->exp_data.scale = 1.0f; - i->exp_data.parent_scale = 1.0f; - i->ex_data_mat = 0; - } -} - -static void rob_chara_bone_data_calculate_bones(rob_chara_bone_data* rob_bone_data, - bone_database_bone* bones) { - bone_database_bones_calculate_count(bones, &rob_bone_data->object_bone_count, - &rob_bone_data->motion_bone_count, &rob_bone_data->total_bone_count, - &rob_bone_data->ik_bone_count, &rob_bone_data->chain_pos); -} - -static void rob_chara_bone_data_set_mats(rob_chara_bone_data* rob_bone_data, - bone_database_bone* a2) { - size_t chain_pos = 0; - size_t ik_bone_count = 0; - size_t total_bone_count = 0; - - mat4* mats = rob_bone_data->mats.begin; - mat4* mats2 = rob_bone_data->mats2.begin; - bone_node* nodes = rob_bone_data->nodes.begin; - for (bone_database_bone* i = a2; i->type != BONE_DATABASE_BONE_END; i++) - switch (i->type) { - case BONE_DATABASE_BONE_ROTATION: - case BONE_DATABASE_BONE_TYPE_1: - case BONE_DATABASE_BONE_POSITION: - (nodes++)->mat = mats++; - - chain_pos++; - total_bone_count++; - break; - case BONE_DATABASE_BONE_POSITION_ROTATION: - (nodes++)->mat = mats++; - - chain_pos++; - ik_bone_count++; - total_bone_count++; - break; - case BONE_DATABASE_BONE_HEAD_IK_ROTATION: - (nodes++)->mat = mats2++; - (nodes++)->mat = mats++; - (nodes++)->mat = mats2++; - - chain_pos += 2; - ik_bone_count++; - total_bone_count += 3; - break; - case BONE_DATABASE_BONE_ARM_IK_ROTATION: - case BONE_DATABASE_BONE_LEGS_IK_ROTATION: - (nodes++)->mat = mats++; - (nodes++)->mat = mats++; - (nodes++)->mat = mats++; - (nodes++)->mat = mats2++; - - chain_pos += 3; - ik_bone_count++; - total_bone_count += 4; - break; - } - - for (bone_node* i = rob_bone_data->nodes.begin; i != rob_bone_data->nodes.end; i++) - i->ex_data_mat = i->mat; - - if (total_bone_count != rob_bone_data->total_bone_count) - printf_debug("Node mismatch\n"); - if (mats - rob_bone_data->mats.begin != rob_bone_data->object_bone_count) - printf_debug("Matrix mismatch\n"); - if (mats2 - rob_bone_data->mats2.begin != rob_bone_data->total_bone_count - rob_bone_data->object_bone_count) - printf_debug("Matrix2 mismatch\n"); - if (ik_bone_count != rob_bone_data->ik_bone_count) - printf_debug("LeafPos mismatch\n"); - if (chain_pos != rob_bone_data->chain_pos) - printf_debug("ChainPos mismatch\n"); -} - -static void rob_chara_bone_data_set_parent_mats(rob_chara_bone_data* rob_bone_data, - uint16_t* parent_indices) { - if (vector_old_length(rob_bone_data->nodes) < 1) - return; - - parent_indices++; - for (bone_node* i = &rob_bone_data->nodes.begin[1]; i != rob_bone_data->nodes.end; i++) - i->parent = &rob_bone_data->nodes.begin[*parent_indices++]; -} - -static void bone_data_parent_load_rob_chara(bone_data_parent* a1) { - rob_chara_bone_data* rob_bone_data = a1->rob_bone_data; - if (!rob_bone_data) - return; - - a1->motion_bone_count = rob_bone_data->motion_bone_count; - a1->ik_bone_count = rob_bone_data->ik_bone_count; - a1->chain_pos = rob_bone_data->chain_pos; - - if (a1->bones.begin) - HeapCMallocFree(HeapCMallocSystem, a1->bones.begin); - - size_t bones_size = rob_bone_data->motion_bone_count; - a1->bones.begin = (bone_data*)HeapCMallocAllocate(HeapCMallocSystem, - bones_size * sizeof(bone_data), "ALLOCsys"); - a1->bones.end = a1->bones.begin + bones_size; - a1->bones.capacity_end = a1->bones.begin + bones_size; - - for (bone_data* i = a1->bones.begin; i != a1->bones.end; i++) - bone_data_init(i); - - a1->global_key_set_count = 6; - a1->field_8 = true; - a1->bone_key_set_count = (uint32_t)((a1->motion_bone_count + a1->ik_bone_count) * 3); -} - -static void bone_data_parent_load_bone_database(bone_data_parent* bone, - bone_database_bone* bones, vec3* common_translation, vec3* translation) { - rob_chara_bone_data* rob_bone_data = bone->rob_bone_data; - size_t chain_pos = 0; - size_t total_bone_count = 0; - size_t ik_bone_count = 0; - - bone_database_bone* v15 = bones; - bone_data* v16 = bone->bones.begin; - for (size_t i = 0; v15->type != BONE_DATABASE_BONE_END; i++, v15++, v16++) { - v16->motion_bone_index = (motion_bone_index)i; - v16->type = (bone_database_bone_type)v15->type; - v16->mirror = v15->mirror; - v16->parent = v15->parent; - v16->flags = v15->flags; - if (v15->type >= BONE_DATABASE_BONE_POSITION_ROTATION) - v16->key_set_count = 6; - else - v16->key_set_count = 3; - v16->base_translation[0] = *common_translation++; - v16->base_translation[1] = *translation++; - v16->node = &rob_bone_data->nodes.begin[total_bone_count]; - v16->has_parent = v15->has_parent; - if (v15->has_parent) - v16->parent_mat = &rob_bone_data->mats.begin[v15->parent]; - else - v16->parent_mat = 0; - - v16->pole_target_mat = 0; - switch (v15->type) { - case BONE_DATABASE_BONE_ROTATION: - case BONE_DATABASE_BONE_TYPE_1: - case BONE_DATABASE_BONE_POSITION: - chain_pos++; - total_bone_count++; - break; - case BONE_DATABASE_BONE_POSITION_ROTATION: - chain_pos++; - ik_bone_count++; - total_bone_count++; - break; - case BONE_DATABASE_BONE_HEAD_IK_ROTATION: - v16->ik_segment_length[0] = (common_translation++)->x; - v16->ik_segment_length[1] = (translation++)->x; - - chain_pos += 2; - ik_bone_count++; - total_bone_count += 3; - break; - case BONE_DATABASE_BONE_ARM_IK_ROTATION: - case BONE_DATABASE_BONE_LEGS_IK_ROTATION: - v16->ik_segment_length[0] = (common_translation++)->x; - v16->ik_segment_length[1] = (translation++)->x; - v16->ik_2nd_segment_length[0] = (common_translation++)->x; - v16->ik_2nd_segment_length[1] = (translation++)->x; - - mat4* pole_target = 0; - if (v15->pole_target) - pole_target = &rob_bone_data->mats.begin[v15->pole_target]; - v16->pole_target_mat = pole_target; - - chain_pos += 3; - ik_bone_count++; - total_bone_count += 4; - break; - } - } - - if (total_bone_count != rob_bone_data->total_bone_count) - printf("Node mismatch\n"); - if (ik_bone_count != bone->ik_bone_count) - printf("LeafPos mismatch\n"); - if (chain_pos != bone->chain_pos) - printf("ChainPos mismatch\n"); -} - -static void bone_data_parent_data_init(bone_data_parent* bone, - rob_chara_bone_data* rob_bone_data) { - bone->rob_bone_data = rob_bone_data; - bone_database_bone* common_bone_data = bone_database_get_bones_by_type(rob_bone_data->base_skeleton_type); - vec3* common_translation = bone_database_get_skeleton_positions_by_type(rob_bone_data->base_skeleton_type); - vec3* translation = bone_database_get_skeleton_positions_by_type(rob_bone_data->skeleton_type); - bone_data_parent_load_rob_chara(bone); - bone_data_parent_load_bone_database(bone, common_bone_data, common_translation, translation); -} - -static void mot_key_data_get_key_set_count(mot_key_data* a1, size_t motion_bone_count, size_t ik_bone_count) { - a1->key_set_count = (motion_bone_count + ik_bone_count) * 3 + 16; -} - -static void mot_key_data_reserve_key_sets(mot_key_data* a1) { - if (a1->key_set.begin) - HeapCMallocFree(HeapCMallocSystem, a1->key_set.begin); - - size_t key_set_count = a1->key_set_count; - a1->key_set.begin = (mot_key_set*)HeapCMallocAllocate(HeapCMallocSystem, - key_set_count * sizeof(mot_key_set), "ALLOCsys"); - a1->key_set.end = a1->key_set.begin + key_set_count; - a1->key_set.capacity_end = a1->key_set.begin + key_set_count; - - for (mot_key_set* i = a1->key_set.begin; i != a1->key_set.end; i++) { - i->type = (mot_key_set_type)0; - i->keys_count = 0; - i->current_key = 0; - i->last_key = 0; - i->frames = 0; - i->values = 0; - } - - if (a1->key_set_data.begin) - HeapCMallocFree(HeapCMallocSystem, a1->key_set_data.begin); - - a1->key_set_data.begin = (float_t*)HeapCMallocAllocate(HeapCMallocSystem, - key_set_count * sizeof(float_t), "ALLOCsys"); - a1->key_set_data.end = a1->key_set_data.begin + key_set_count; - a1->key_set_data.capacity_end = a1->key_set_data.begin + key_set_count; - - for (float_t* i = a1->key_set_data.begin; i != a1->key_set_data.end; i++) - *i = 0.0f; - - a1->mot.key_sets = a1->key_set.begin; - a1->key_sets_ready = true; -} - -static void mot_key_data_init_key_sets(mot_key_data* a1, bone_database_skeleton_type type, - size_t motion_bone_count, size_t ik_bone_count) { - mot_key_data_get_key_set_count(a1, motion_bone_count, ik_bone_count); - mot_key_data_reserve_key_sets(a1); - a1->skeleton_type = type; -} - -static void sub_14040F650(struc_313* a1, size_t a2) { - uint32_t* v5 = a1->bitfield.end; - uint32_t* v6 = a1->bitfield.begin; - size_t v7 = (a2 + 31) >> 5; - if ((ssize_t)v7 < v5 - v6) { - uint32_t* v8 = &v6[v7]; - if (v8 == v6) - a1->bitfield.end = v6; - else if (v8 != v5) { - memmove(&v6[v7], v5, 0); - a1->bitfield.end = v8; - } - } - a1->motion_bone_count = a2; - - a2 &= 0x1F; - if (a2) - a1->bitfield.begin[v7 - 1] &= (1 << a2) - 1; -} - -static struc_314* sub_1404122C0(struc_313* a1, struc_314* a2, struc_314* a3, struc_314* a4) { - uint32_t* v4 = a1->bitfield.begin; - size_t v7 = a1->motion_bone_count; - - struc_314 v8; - v8.field_0 = a1->bitfield.begin; - v8.field_8 = 0; - if (v7) { - size_t v12 = (a3->field_0 - v4) * 32 + a3->field_8; - v8.field_0 = &v4[v12 >> 5]; - v8.field_8 = v12 & 0x1F; - } - - struc_314 v16; - v16.field_0 = a1->bitfield.begin; - v16.field_8 = 0; - if (v7) { - size_t v17 = (a4->field_0 - v4) * 32 + a4->field_8; - v16.field_0 = &v4[v17 >> 5]; - v16.field_8 = v17 & 0x1F; - } - - if (v8.field_0 != v16.field_0 || v8.field_8 != v16.field_8) { - size_t v19 = 0; - if (v7) { - v4 += v7 >> 5; - v19 = a1->motion_bone_count & 0x1F; - } - - struc_314 v20 = v16; - struc_314 v22 = v8; - while (v20.field_0 != v4 || v20.field_8 != v19) { - if (*v20.field_0 & (1 << v20.field_8) ) - *v22.field_0 |= 1 << v22.field_8; - else - *v22.field_0 &= ~(1 << v22.field_8); - - if (v22.field_8 >= 0x1F) { - v22.field_8 = 0; - v22.field_0++; - } - else - v22.field_8++; - - if (v20.field_8 >= 0x1F) { - v20.field_8 = 0; - v20.field_0++; - } - else - v20.field_8++; - } - sub_14040F650(a1, (v22.field_0 - a1->bitfield.begin) * 32 + v22.field_8); - } - - size_t v18 = (v8.field_0 - v4) * 32 + v8.field_8; - a2->field_0 = &a1->bitfield.begin[v18 >> 5]; - a2->field_8 = v18 & 0x1F; - return a2; -} - -static void sub_1404119A0(struc_313* a1) { - uint32_t* v1 = a1->bitfield.begin; - size_t v2 = a1->motion_bone_count; - struc_314 v3; - v3.field_0 = &v1[v2 >> 5]; - v3.field_8 = v2 & 0x1F; - struc_314 v4; - v4.field_0 = v1; - v4.field_8 = 0; - struc_314 v5; - sub_1404122C0(a1, &v5, &v4, &v3); -} - -static uint32_t* sub_14040BDB0(uint32_t* a1, uint32_t* a2, uint32_t* a3) { - for (; a1 != a2; a1++, a3++) - *a3 = *a1; - return a3; -} - -static void sub_14040BD00(uint32_t* a1, size_t a2, uint32_t* a3) { - for (; a2; a1++, a2--) - *a1 = *a3; -} - -static void sub_14040E690(struc_313* a1, uint32_t** a2, uint32_t* a3, size_t a4, uint32_t* a5) { - size_t v8 = a3 - a1->bitfield.begin; - if (!a4) { - *a2 = &a1->bitfield.begin[v8]; - return; - } - - uint32_t* v9 = a1->bitfield.end; - if (a1->bitfield.capacity_end - v9 >= (ssize_t)a4) { - if (v9 - a3 >= (ssize_t)a4) { - uint32_t v20 = *a5; - size_t v21 = a4; - uint32_t* v22 = &v9[-(ssize_t)a4]; - a1->bitfield.end = sub_14040BDB0(v22, v9, v9); - memmove(&v9[-(v22 - a3)], a3, 4 * (v22 - a3)); - for (uint32_t* i = a3; i != &a3[v21]; i++) - *i = v20; - } - else { - uint32_t v17 = *a5; - uint32_t v26 = *a5; - sub_14040BDB0(a3, v9, &a3[a4]); - sub_14040BD00(a1->bitfield.end, a4 - (a1->bitfield.end - a3), &v26); - a1->bitfield.end += a4; - uint32_t* v18 = &a1->bitfield.end[-(ssize_t)a4]; - for (uint32_t* i = a3; i != v18; i++) - *i = v17; - } - } - else { - uint32_t* v7 = a1->bitfield.begin; - size_t v10 = v9 - v7; - size_t v11 = a1->bitfield.capacity_end - v7; - size_t v12; - if (UINT64_MAX / sizeof(uint32_t) - (v11 >> 1) >= v11) - v12 = (v11 >> 1) + v11; - else - v12 = 0; - if (v12 < v10 + a4) - v12 = v10 + a4; - uint32_t* v14 = (uint32_t*)HeapCMallocAllocate(HeapCMallocSystem, - sizeof(uint32_t) * v12, "ALLOCsys"); - size_t v15 = a3 - a1->bitfield.begin; - sub_14040BD00(&v14[v15], a4, a5); - sub_14040BDB0(a1->bitfield.begin, a3, v14); - sub_14040BDB0(a3, a1->bitfield.end, &v14[v15 + a4]); - size_t v16 = vector_old_length(a1->bitfield) + a4; - if (a1->bitfield.begin) - HeapCMallocFree(HeapCMallocSystem, a1->bitfield.begin); - a1->bitfield.capacity_end = &v14[v12]; - a1->bitfield.end = &v14[v16]; - a1->bitfield.begin = v14; - } - *a2 = &a1->bitfield.begin[v8]; -} - -static void sub_140413F30(struc_313* a1, size_t a2, uint32_t* a3) { - uint32_t* v3 = a1->bitfield.end; - uint32_t* v5 = a1->bitfield.begin; - size_t v6 = v3 - v5; - if (v6 > a2) { - uint32_t* v7 = &v5[a2]; - if (v7 == v5) - a1->bitfield.end = v5; - else if (v7 != v3) - a1->bitfield.end = v7; - } - else if (v6 < a2) { - uint32_t* a2a; - sub_14040E690(a1, &a2a, v3, a2 - v6, a3); - } -} - -static size_t sub_14040EB30(struc_313* a1, struc_314* a2, size_t a3) { - size_t v5 = (a2->field_0 - a1->bitfield.begin) * 32 + a2->field_8; - if (!a3) - return v5; - - size_t v6 = a1->motion_bone_count; - uint32_t v21 = 0; - sub_140413F30(a1, (v6 + a3 + 31) >> 5, &v21); - v6 = a1->motion_bone_count; - if (!v6) { - a1->motion_bone_count = a3; - return v5; - } - - size_t v11 = a3 + v6; - uint32_t* v9 = a1->bitfield.begin; - struc_314 v14; - v14.field_0 = &v9[v6 >> 5]; - v14.field_8 = v6 & 0x1F; - a1->motion_bone_count = v11; - struc_314 v16; - v16.field_0 = &v9[v11 >> 5]; - v16.field_8 = v11 & 0x1F; - struc_314 v12; - v12.field_0 = &v9[v5 >> 5]; - v12.field_8 = v5 & 0x1F; - while (v12.field_0 != v14.field_0 || v12.field_8 != v14.field_8) { - if (v14.field_8) - v14.field_8--; - else { - v14.field_8 = 31; - v14.field_0--; - } - - if (v16.field_8) - v16.field_8--; - else { - v16.field_8 = 31; - v16.field_0--; - } - - if (*v14.field_0 & (1 << v14.field_8)) - *v16.field_0 |= 1 << v16.field_8; - else - *v16.field_0 &= ~(1 << v16.field_8); - } - return v5; -} - -static struc_314* sub_14040E980(struc_313* a1, struc_314* a2, struc_314* a3, size_t a4, bool* a5) { - struc_314 v20 = *a3; - size_t v8 = sub_14040EB30(a1, &v20, a4); - uint32_t* v9 = a1->bitfield.begin; - - size_t v10 = v8 + a4; - struc_314 v12; - v12.field_0 = &v9[v10 >> 5]; - v12.field_8 = v10 & 0x1F; - v20.field_0 = &v9[v8 >> 5]; - v20.field_8 = v8 & 0x1F; - while (v20.field_0 != v12.field_0 || v20.field_8 != v12.field_8) { - if (*a5) - *v20.field_0 |= (1 << v20.field_8); - else - *v20.field_0 &= ~(1 << v20.field_8); - - if (v20.field_8 >= 0x1F) { - v20.field_8 = 0; - v20.field_0++; - } - else - v20.field_8++; - } - - uint32_t* v17 = a1->bitfield.begin; - a2->field_0 = &v17[v8 >> 5]; - a2->field_8 = v8 & 0x1F; - return a2; -} - -static void sub_140414550(struc_313* a1, size_t a2, bool a3) { - struc_314 a3a; - struc_314 a4; - struc_314 a2a; - - size_t v3 = a1->motion_bone_count; - if (a2 <= v3) { - if (a2 < v3) { - uint32_t* v6 = a1->bitfield.begin; - a4.field_0 = &v6[v3 >> 5]; - a4.field_8 = v3 & 0x1F; - a3a.field_0 = &v6[a2 >> 5]; - a3a.field_8 = a2 & 0x1F; - sub_1404122C0(a1, &a2a, &a3a, &a4); - } - } - else { - uint32_t* v5 = a1->bitfield.begin; - a3a.field_0 = &v5[v3 >> 5]; - a3a.field_8 = v3 & 0x1F; - a4 = a3a; - sub_14040E980(a1, &a3a, &a4, a2 - v3, &a3); - } -} - -static void sub_1404146F0(struc_240* a1) { - struc_314 v1; - v1.field_0 = a1->field_8.bitfield.begin; - v1.field_8 = 0; - int32_t v2 = MOTION_BONE_N_HARA_CP; - size_t v5 = a1->field_8.motion_bone_count; - uint32_t* v6 = a1->field_8.bitfield.begin; - while (true) { - struc_314 v7; - v7.field_0 = &v6[v5 >> 5]; - v7.field_8 = v5 & 0x1F; - if (v1.field_0 == v7.field_0 && v1.field_8 == v7.field_8) - break; - - if (a1->bone_check_func((motion_bone_index)v2++)) - *v1.field_0 |= 1 << v1.field_8; - else - *v1.field_0 &= ~(1 << v1.field_8); - - if (v1.field_8 >= 0x1F) { - v1.field_8 = 0; - v1.field_0++; - } - else - v1.field_8++; - } -} - -static void sub_140413350(struc_240* a1, bool(*bone_check_func)(motion_bone_index), size_t motion_bone_count) { - a1->bone_check_func = bone_check_func; - a1->motion_bone_count = motion_bone_count; - sub_1404119A0(&a1->field_8); - sub_140414550(&a1->field_8, motion_bone_count, 0); - sub_1404146F0(a1); -} - -static void motion_blend_mot_load_bone_data(motion_blend_mot* a1, rob_chara_bone_data* rob_bone_data, - bool(*bone_check_func)(motion_bone_index)) { - bone_data_parent_data_init(&a1->bone_data, rob_bone_data); - mot_key_data_init_key_sets( - &a1->mot_key_data, - a1->bone_data.rob_bone_data->base_skeleton_type, - a1->bone_data.motion_bone_count, - a1->bone_data.ik_bone_count); - sub_140413350(&a1->field_0, bone_check_func, a1->bone_data.motion_bone_count); -} - -static void sub_140414900(struc_308* a1, mat4* mat) { - if (mat) { - a1->mat = *mat; - a1->field_8C = true; - } - else { - a1->mat = mat4_identity; - a1->field_8C = false; - } -} - -static void bone_data_mult_1_ik_hands(bone_data* a1, vec3* a2) { - mat4 mat; - mat4_transpose(a1->node[0].mat, &mat); - - vec3 v15; - mat4_mult_vec3_inv_trans(&mat, a2, &v15); - - float_t len; - vec3_length_squared(v15, len); - if (len <= 0.000001f) - return; - - len = sqrtf(len); - if (len != 0.0f) - vec3_mult_scalar(v15, 1.0f / len, v15); - - vec3 v17; - v17.x = -v15.z * v15.x - v15.z; - v17.y = -v15.y * v15.z; - v17.z = v15.x * v15.x + v15.y * v15.y + v15.x; - vec3_length_squared(v17, len); - if (len <= 0.000001f) - return; - - len = sqrtf(len); - if (len != 0.0f) { - vec3_mult_scalar(v17, 1.0f / len, v17); - } - - vec3 v16; - vec3_cross(v17, v15, v16); - - mat4 rot_mat = mat4_identity; - *(vec3*)&rot_mat.row0 = v15; - *(vec3*)&rot_mat.row1 = v16; - *(vec3*)&rot_mat.row2 = v17; - - mat4_mult(&rot_mat, &mat, &mat); - mat4_transpose(&mat, a1->node[0].mat); -} - -static void bone_data_mult_1_ik_hands_2(bone_data* a1, vec3* a2, float_t a3) { - float_t len; - vec3 v8; - - mat4 mat; - mat4_transpose(a1->node[0].mat, &mat); - mat4_mult_vec3_inv_trans(&mat, a2, &v8); - v8.x = 0.0f; - vec3_length(v8, len); - if (len <= 0.000001f) - return; - - vec3_mult_scalar(v8, 1.0f / len, v8); - float_t angle = atan2f(v8.z, v8.y); - mat4_rotate_x_mult(&mat, angle * a3, &mat); - mat4_transpose(&mat, a1->node[0].mat); -} - -static void bone_data_mult_1_ik_legs(bone_data* a1, vec3* a2) { - vec3 v9 = *a2; - - float_t len; - vec3_length_squared(v9, len); - if (len <= 0.000001f) - return; - - len = sqrtf(len); - if (len != 0.0f) - vec3_mult_scalar(v9, 1.0f / len, v9); - - vec3 v8; - v8.x = -v9.z * v9.x; - v8.y = -v9.z * v9.y - v9.z; - v8.z = v9.y * v9.y - v9.x * -v9.x + v9.y; - - vec3_length_squared(v8, len); - if (len <= 0.000001f) - return; - - len = sqrtf(len); - if (len != 0.0f) - vec3_mult_scalar(v8, 1.0f / len, v8); - - vec3 v10; - vec3_cross(v9, v8, v10); - - mat4 rot_mat = mat4_identity; - *(vec3*)&rot_mat.row0 = v10; - *(vec3*)&rot_mat.row1 = v9; - *(vec3*)&rot_mat.row2 = v8; - - mat4 mat; - mat4_transpose(a1->node[0].mat, &mat); - mat4_mult(&rot_mat, &mat, &mat); - mat4_transpose(&mat, a1->node[0].mat); -} - -static bool bone_data_mult_1_ik(bone_data* a1, bone_data* a2) { - bone_node* v5; - vec3 v30; - mat4 mat; - - switch (a1->motion_bone_index) { - case MOTION_BONE_N_SKATA_L_WJ_CD_EX: - v5 = a2[MOTION_BONE_C_KATA_L].node; - mat4_transpose(v5[2].mat, &mat); - mat4_get_translation(&mat, &v30); - bone_data_mult_1_ik_hands(a1, &v30); - break; - case MOTION_BONE_N_SKATA_R_WJ_CD_EX: - v5 = a2[MOTION_BONE_C_KATA_R].node; - mat4_transpose(v5[2].mat, &mat); - mat4_get_translation(&mat, &v30); - bone_data_mult_1_ik_hands(a1, &v30); - break; - case MOTION_BONE_N_SKATA_B_L_WJ_CD_CU_EX: - v5 = a2[MOTION_BONE_N_UP_KATA_L_EX].node; - mat4_transpose(v5[0].mat, &mat); - mat4_get_translation(&mat, &v30); - bone_data_mult_1_ik_hands_2(a1, &v30, 0.333f); - break; - case MOTION_BONE_N_SKATA_B_R_WJ_CD_CU_EX: - v5 = a2[MOTION_BONE_N_UP_KATA_R_EX].node; - mat4_transpose(v5[0].mat, &mat); - mat4_get_translation(&mat, &v30); - bone_data_mult_1_ik_hands_2(a1, &v30, 0.333f); - break; - case MOTION_BONE_N_SKATA_C_L_WJ_CD_CU_EX: - v5 = a2[MOTION_BONE_N_UP_KATA_L_EX].node; - mat4_transpose(v5[0].mat, &mat); - mat4_get_translation(&mat, &v30); - bone_data_mult_1_ik_hands_2(a1, &v30, 0.5f); - break; - case MOTION_BONE_N_SKATA_C_R_WJ_CD_CU_EX: - v5 = a2[MOTION_BONE_N_UP_KATA_R_EX].node; - mat4_transpose(v5[0].mat, &mat); - mat4_get_translation(&mat, &v30); - bone_data_mult_1_ik_hands_2(a1, &v30, 0.5f); - break; - case MOTION_BONE_N_MOMO_A_L_WJ_CD_EX: - v5 = a2[MOTION_BONE_CL_MOMO_L].node; - mat4_transpose(v5[2].mat, &mat); - mat4_get_translation(&mat, &v30); - mat4_transpose(a1->node[0].mat, &mat); - mat4_mult_vec3_inv_trans(&mat, &v30, &v30); - vec3_negate(v30, v30); - bone_data_mult_1_ik_legs(a1, &v30); - break; - case MOTION_BONE_N_MOMO_A_R_WJ_CD_EX: - v5 = a2[MOTION_BONE_CL_MOMO_R].node; - mat4_transpose(v5[2].mat, &mat); - mat4_get_translation(&mat, &v30); - mat4_transpose(a1->node[0].mat, &mat); - mat4_mult_vec3_inv_trans(&mat, &v30, &v30); - vec3_negate(v30, v30); - bone_data_mult_1_ik_legs(a1, &v30); - break; - case MOTION_BONE_N_HARA_CD_EX: - v5 = a2[MOTION_BONE_KL_MUNE_B_WJ].node; - mat4_transpose(v5[0].mat, &mat); - mat4_get_translation(&mat, &v30); - mat4_transpose(a1->node[0].mat, &mat); - mat4_mult_vec3_inv_trans(&mat, &v30, &v30); - bone_data_mult_1_ik_legs(a1, &v30); - break; - default: - return false; - } - return true; -} - -static float_t sub_1401E9D10(float_t a1) { - bool neg; - if (a1 < 0.0f) { - a1 = -a1; - neg = true; - } - else - neg = false; - - a1 = fmodf(a1 + (float_t)M_PI, (float_t)(M_PI * 2.0)) - (float_t)M_PI; - if (neg) - return -a1; - else - return a1; -} - -static bool bone_data_mult_1_exp_data(bone_data* a1, bone_node_expression_data* exp_data, bone_data* a3) { - float_t v10; - bone_node* v11; - vec3 v15; - mat4 dst; - mat4 mat; - - bool ret = true; - switch (a1->motion_bone_index) { - case MOTION_BONE_N_EYE_L_WJ_EX: - exp_data->rotation.x = -a3[MOTION_BONE_KL_EYE_L].node[0].exp_data.rotation.x; - exp_data->rotation.y = a3[MOTION_BONE_KL_EYE_L].node[0].exp_data.rotation.y * (float_t)(-1.0 / 2.0); - break; - case MOTION_BONE_N_EYE_R_WJ_EX: - exp_data->rotation.x = -a3[MOTION_BONE_KL_EYE_R].node[0].exp_data.rotation.x; - exp_data->rotation.y = a3[MOTION_BONE_KL_EYE_R].node[0].exp_data.rotation.y * (float_t)(-1.0 / 2.0); - break; - case MOTION_BONE_N_KUBI_WJ_EX: - mat4_transpose(&a3[MOTION_BONE_CL_KAO].rot_mat[0], &dst); - mat4_transpose(&a3[MOTION_BONE_CL_KAO].rot_mat[1], &mat); - mat4_mult(&mat, &dst, &dst); - mat4_get_rotation(&dst, &v15); - v10 = v15.z; - if (v10 < (float_t)-M_PI_2) - v10 += (float_t)(M_PI * 2.0); - exp_data->rotation.y = (v10 - (float_t)M_PI_2) * 0.2f; - break; - case MOTION_BONE_N_HITO_L_EX: - case MOTION_BONE_N_HITO_R_EX: - exp_data->rotation.z = (float_t)(-9.0 * DEG_TO_RAD); - break; - case MOTION_BONE_N_KO_L_EX: - exp_data->rotation.x = (float_t)(-8.0 * DEG_TO_RAD); - exp_data->rotation.z = (float_t)(16.0 * DEG_TO_RAD); - break; - case MOTION_BONE_N_KUSU_L_EX: - case MOTION_BONE_N_KUSU_R_EX: - exp_data->rotation.z = (float_t)(9.0 * DEG_TO_RAD); - break; - case MOTION_BONE_N_NAKA_L_EX: - case MOTION_BONE_N_NAKA_R_EX: - exp_data->rotation.z = (float_t)(-1.0 * DEG_TO_RAD); - break; - case MOTION_BONE_N_OYA_L_EX: - exp_data->rotation.x = (float_t)(75.0 * DEG_TO_RAD); - exp_data->rotation.y = (float_t)(12.0 * DEG_TO_RAD); - exp_data->rotation.z = (float_t)(-24.0 * DEG_TO_RAD); - break; - case MOTION_BONE_N_STE_L_WJ_EX: - exp_data->rotation.x = a3[MOTION_BONE_KL_TE_L_WJ].node[0].exp_data.rotation.x; - break; - case MOTION_BONE_N_SUDE_L_WJ_EX: - case MOTION_BONE_N_SUDE_B_L_WJ_EX: - v11 = &a3[MOTION_BONE_KL_TE_L_WJ].node[0]; - exp_data->rotation.x = sub_1401E9D10(v11->exp_data.rotation.x) * (float_t)(1.0 / 3.0); - break; - case MOTION_BONE_N_SUDE_R_WJ_EX: - case MOTION_BONE_N_SUDE_B_R_WJ_EX: - v11 = &a3[MOTION_BONE_KL_TE_R_WJ].node[0]; - exp_data->rotation.x = sub_1401E9D10(v11->exp_data.rotation.x) * (float_t)(1.0 / 3.0); - break; - case MOTION_BONE_N_HIJI_L_WJ_EX: - exp_data->rotation.z = a3[MOTION_BONE_C_KATA_L].node[2].exp_data.rotation.z * (float_t)(1.0 / 2.0); - break; - case MOTION_BONE_N_UP_KATA_L_EX: - case MOTION_BONE_N_UP_KATA_R_EX: - break; - case MOTION_BONE_N_KO_R_EX: - exp_data->rotation.x = (float_t)(8.0 * DEG_TO_RAD); - exp_data->rotation.z = (float_t)(16.0 * DEG_TO_RAD); - break; - case MOTION_BONE_N_OYA_R_EX: - exp_data->rotation.x = (float_t)(-75.0 * DEG_TO_RAD); - exp_data->rotation.y = (float_t)(-12.0 * DEG_TO_RAD); - exp_data->rotation.z = (float_t)(-24.0 * DEG_TO_RAD); - break; - case MOTION_BONE_N_STE_R_WJ_EX: - exp_data->rotation.x = a3[MOTION_BONE_KL_TE_R_WJ].node[0].exp_data.rotation.x; - break; - case MOTION_BONE_N_HIJI_R_WJ_EX: - exp_data->rotation.z = a3[MOTION_BONE_C_KATA_R].node[2].exp_data.rotation.z * (float_t)(1.0 / 2.0); - break; - case MOTION_BONE_N_HIZA_L_WJ_EX: - exp_data->rotation.z = a3[MOTION_BONE_CL_MOMO_L].node[2].exp_data.rotation.z * (float_t)(1.0 / 2.0); - break; - case MOTION_BONE_N_HIZA_R_WJ_EX: - exp_data->rotation.z = a3[MOTION_BONE_CL_MOMO_R].node[2].exp_data.rotation.z * (float_t)(1.0 / 2.0); - break; - case MOTION_BONE_N_MOMO_B_L_WJ_EX: - case MOTION_BONE_N_MOMO_C_L_WJ_EX: - v11 = &a3[MOTION_BONE_CL_MOMO_L].node[0]; - exp_data->rotation.y = sub_1401E9D10(v11->exp_data.rotation.y - + v11->exp_data.rotation.x) * (float_t)(1.0 / 3.0); - break; - case MOTION_BONE_N_MOMO_B_R_WJ_EX: - case MOTION_BONE_N_MOMO_C_R_WJ_EX: - v11 = &a3[MOTION_BONE_CL_MOMO_R].node[0]; - exp_data->rotation.y = sub_1401E9D10(v11->exp_data.rotation.y - + v11->exp_data.rotation.x) * (float_t)(1.0 / 3.0); - break; - case MOTION_BONE_N_HARA_B_WJ_EX: - exp_data->rotation.y = a3[MOTION_BONE_CL_MUNE].node[0].exp_data.rotation.y * (float_t)(1.0 / 3.0) - + a3[MOTION_BONE_KL_KOSI_Y].node[0].exp_data.rotation.y * (float_t)(2.0 / 3.0); - break; - case MOTION_BONE_N_HARA_C_WJ_EX: - exp_data->rotation.y = a3[MOTION_BONE_CL_MUNE].node[0].exp_data.rotation.y * (float_t)(2.0 / 3.0) - + a3[MOTION_BONE_KL_KOSI_Y].node[0].exp_data.rotation.y * (float_t)(1.0 / 3.0); - break; - default: - ret = false; - break; - } - return ret; -} - -void bone_data_mult_ik(bone_data* a1, int32_t skeleton_select) { - if (a1->type < BONE_DATABASE_BONE_HEAD_IK_ROTATION) - return; - - mat4 mat; - mat4_transpose(a1->node[0].mat, &mat); - - vec3 v30; - mat4_mult_vec3_inv_trans(&mat, &a1->ik_target, &v30); - float_t v6; - float_t v8; - vec2_length_squared(*(vec2*)&v30, v6); - vec3_length_squared(v30, v8); - float_t v9 = sqrtf(v6); - float_t v10 = sqrtf(v8); - mat4 rot_mat; - if (v9 > 0.000001f && v8 > 0.000001f) { - mat4_rotate_z_sin_cos(v30.y / v9, v30.x / v9, &rot_mat); - mat4_rotate_y_mult_sin_cos(&rot_mat, -v30.z / v10, v9 / v10, &rot_mat); - mat4_mult(&rot_mat, &mat, &mat); - } - else - rot_mat = mat4_identity; - - if (a1->pole_target_mat) { - mat4 pole_target_mat; - mat4_transpose(a1->pole_target_mat, &pole_target_mat); - vec3 pole_target; - mat4_get_translation(&pole_target_mat, &pole_target); - mat4_mult_vec3_inv_trans(&mat, &pole_target, &pole_target); - float_t pole_target_length; - vec2_length(*(vec2*)&pole_target.y, pole_target_length); - if (pole_target_length > 0.000001f) { - vec3_mult_scalar(pole_target, 1.0f / pole_target_length, pole_target); - float_t cos_val = pole_target.y; - float_t sin_val = pole_target.z; - mat4_rotate_x_mult_sin_cos(&mat, sin_val, cos_val, &mat); - mat4_rotate_x_mult_sin_cos(&rot_mat, sin_val, cos_val, &rot_mat); - } - } - - if (a1->type == BONE_DATABASE_BONE_HEAD_IK_ROTATION) { - mat4_transpose(&rot_mat, &a1->rot_mat[1]); - mat4_transpose(&mat, a1->node[1].mat); - mat4_translate_mult(&mat, a1->ik_segment_length[skeleton_select], 0.0f, 0.0f, &mat); - mat4_transpose(&mat, a1->node[2].mat); - return; - } - - float_t v20 = a1->ik_2nd_segment_length[skeleton_select]; - float_t v21 = a1->ik_segment_length[skeleton_select]; - float_t v22; - float_t v23; - float_t v24; - float_t v25; - if (v8 > 0.000001f) { - float_t arm_length = a1->arm_length; - if (arm_length > 0.0001f) { - float_t v27 = (v21 + v20) * arm_length; - if (v10 > v27) { - v10 = v27; - v8 = v27 * v27; - } - } - float_t v28 = (v8 - v20 * v20) / v21; - v23 = (v28 + v21) / (2.0f * v10); - v22 = (v28 - v21) / (2.0f * v20); - - v23 = clamp_def(v23, -1.0f, 1.0f); - v22 = clamp_def(v22, -1.0f, 1.0f); - - v24 = sqrtf(1.0f - v23 * v23); - v25 = sqrtf(1.0f - v22 * v22); - if (a1->type == BONE_DATABASE_BONE_LEGS_IK_ROTATION) - v24 = -v24; - else - v25 = -v25; - } - else { - v22 = -1.0f; - v23 = 1.0f; - v24 = 0.0f; - v25 = 0.0f; - } - - mat4_rotate_z_mult_sin_cos(&mat, v24, v23, &mat); - mat4_transpose(&mat, a1->node[1].mat); - mat4_rotate_z_mult_sin_cos(&rot_mat, v24, v23, &rot_mat); - mat4_transpose(&rot_mat, &a1->rot_mat[1]); - mat4_translate_mult(&mat, v21, 0.0f, 0.0f, &mat); - mat4_rotate_z_mult_sin_cos(&mat, v25, v22, &mat); - mat4_transpose(&mat, a1->node[2].mat); - mat4_rotate_z_sin_cos(v25, v22, &rot_mat); - mat4_transpose(&rot_mat, &a1->rot_mat[2]); - mat4_translate_mult(&mat, v20, 0.0f, 0.0f, &mat); - mat4_transpose(&mat, a1->node[3].mat); -} - -static void bone_data_mult_0(bone_data* a1, int32_t skeleton_select) { - if (a1->flags != 0) - return; - - mat4 mat; - if (a1->has_parent) - mat4_transpose(a1->parent_mat, &mat); - else - mat = mat4_identity; - - if (a1->type == BONE_DATABASE_BONE_POSITION) { - mat4_translate_mult(&mat, &a1->trans, &mat); - a1->rot_mat[0] = mat4_identity; - } - else if (a1->type == BONE_DATABASE_BONE_TYPE_1) { - mat4_mult_vec3_inv_trans(&mat, &a1->trans, &a1->trans); - mat4_translate_mult(&mat, &a1->trans, &mat); - a1->rot_mat[0] = mat4_identity; - } - else { - mat4 rot_mat; - if (a1->type == BONE_DATABASE_BONE_POSITION_ROTATION) { - mat4_transpose(&a1->rot_mat[0], &rot_mat); - mat4_rotate_z_mult(&rot_mat, a1->rotation.z, &rot_mat); - } - else { - a1->trans = a1->base_translation[skeleton_select]; - mat4_rotate_z(a1->rotation.z, &rot_mat); - } - - mat4_rotate_y_mult(&rot_mat, a1->rotation.y, &rot_mat); - - if (a1->motion_bone_index == MOTION_BONE_KL_EYE_L - || a1->motion_bone_index == MOTION_BONE_KL_EYE_R) { - if (a1->rotation.x > 0.0) - a1->rotation.x *= a1->eyes_xrot_adjust_pos; - else if (a1->rotation.x < 0.0) - a1->rotation.x *= a1->eyes_xrot_adjust_neg; - } - - mat4_rotate_x_mult(&rot_mat, a1->rotation.x, &rot_mat); - mat4_translate_mult(&mat, &a1->trans, &mat); - mat4_mult(&rot_mat, &mat, &mat); - mat4_transpose(&rot_mat, &a1->rot_mat[0]); - } - - mat4_transpose(&mat, a1->node[0].mat); - - bone_data_mult_ik(a1, skeleton_select); -} - -void bone_data_mult_1(bone_data* a1, mat4* parent_mat, bone_data* a3, bool a4) { - mat4 mat; - mat4 rot_mat; - if (a1->has_parent) - mat4_transpose(a1->parent_mat, &mat); - else - mat4_transpose(parent_mat, &mat); - - if (a1->type != BONE_DATABASE_BONE_TYPE_1 && a1->type != BONE_DATABASE_BONE_POSITION) { - if (a1->type != BONE_DATABASE_BONE_POSITION_ROTATION) - a1->trans = a1->base_translation[1]; - - mat4_translate_mult(&mat, &a1->trans, &mat); - if (a4) { - a1->node[0].exp_data.rotation = 0.0f; - if (!a1->flags) { - mat4_transpose(&a1->rot_mat[0], &rot_mat); - mat4_get_rotation(&rot_mat, &a1->node[0].exp_data.rotation); - } - else if (bone_data_mult_1_exp_data(a1, &a1->node[0].exp_data, a3)) { - mat4_rotate(&a1->node[0].exp_data.rotation, &rot_mat); - mat4_transpose(&rot_mat, &a1->rot_mat[0]); - } - else { - mat4_transpose(&mat, a1->node[0].mat); - - if (bone_data_mult_1_ik(a1, a3)) { - mat4 dst; - mat4_invrot_normalized(&mat, &rot_mat); - mat4_transpose(a1->node[0].mat, &dst); - mat4_mult(&dst, &rot_mat, &rot_mat); - mat4_clear_trans(&rot_mat, &rot_mat); - mat4_get_rotation(&rot_mat, &a1->node[0].exp_data.rotation); - mat4_transpose(&rot_mat, &a1->rot_mat[0]); - } - else - a1->rot_mat[0] = mat4_identity; - } - } - - mat4_transpose(&a1->rot_mat[0], &rot_mat); - mat4_mult(&rot_mat, &mat, &mat); - - } - else { - mat4_translate_mult(&mat, &a1->trans, &mat); - if (a4) - a1->node[0].exp_data.rotation = 0.0f; - } - - mat4_transpose(&mat, a1->node[0].mat); - if (a4) { - a1->node[0].exp_data.position = a1->trans; - bone_node_expression_data_reset_scale(&a1->node[0].exp_data); - } - - if (a1->type < BONE_DATABASE_BONE_HEAD_IK_ROTATION) - return; - - mat4_transpose(&a1->rot_mat[1], &rot_mat); - mat4_mult(&rot_mat, &mat, &mat); - mat4_transpose(&mat, a1->node[1].mat); - mat4_translate_mult(&mat, a1->ik_segment_length[1], 0.0f, 0.0f, &mat); - - if (a1->type == BONE_DATABASE_BONE_HEAD_IK_ROTATION) { - mat4_transpose(&mat, a1->node[2].mat); - if (!a4) - return; - - a1->node[1].exp_data.position = 0.0f; - mat4_transpose(&a1->rot_mat[1], &rot_mat); - mat4_get_rotation(&rot_mat, &a1->node[1].exp_data.rotation); - bone_node_expression_data_reset_scale(&a1->node[1].exp_data); - bone_node_expression_data_set_position_rotation(&a1->node[2].exp_data, - a1->ik_segment_length[1], 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); - } - else { - mat4_transpose(&a1->rot_mat[2], &rot_mat); - mat4_mult(&rot_mat, &mat, &mat); - mat4_transpose(&mat, a1->node[2].mat); - mat4_translate_mult(&mat, a1->ik_2nd_segment_length[1], 0.0f, 0.0f, &mat); - mat4_transpose(&mat, a1->node[3].mat); - if (!a4) - return; - - a1->node[1].exp_data.position = 0.0f; - mat4_transpose(&a1->rot_mat[1], &rot_mat); - mat4_get_rotation(&rot_mat, &a1->node[1].exp_data.rotation); - bone_node_expression_data_reset_scale(&a1->node[1].exp_data); - a1->node[2].exp_data.position.x = a1->ik_segment_length[1]; - *(vec2*)&a1->node[2].exp_data.position.y = 0.0f; - mat4_transpose(&a1->rot_mat[2], &rot_mat); - mat4_get_rotation(&rot_mat, &a1->node[2].exp_data.rotation); - bone_node_expression_data_reset_scale(&a1->node[2].exp_data); - bone_node_expression_data_set_position_rotation(&a1->node[3].exp_data, - a1->ik_2nd_segment_length[1], 0.0f, 0.0f, 0.0f, 0.0f, 0.0f); - } -} - -void bone_data_mult_2(rob_chara_bone_data* rob_bone_data, mat4* mat) { - for (bone_node* i = rob_bone_data->nodes.begin; i != rob_bone_data->nodes.end; i++) - mat4_mult(i->mat, mat, i->mat); - sub_140414900(&rob_bone_data->motion_loaded.head->next->value->field_4F8, mat); -} - -static void sub_140413EB0(struc_308* a1) { - a1->field_8C = false; - a1->field_4C = a1->mat; - a1->mat = mat4_identity; -} - -static void sub_1404117F0(motion_blend_mot* a1) { - sub_140413EB0(&a1->field_4F8); - int32_t skeleton_select = a1->mot_key_data.skeleton_select; - for (bone_data* i = a1->bone_data.bones.begin; i != a1->bone_data.bones.end; i++) - bone_data_mult_0(i, skeleton_select); -} - -static void sub_14040FBF0(motion_blend_mot* a1, float_t a2) { - bone_data* b_n_hara_cp = &a1->bone_data.bones.begin[MOTION_BONE_N_HARA_CP]; - bone_data* b_kg_hara_y = &a1->bone_data.bones.begin[MOTION_BONE_KG_HARA_Y]; - bone_data* b_kl_hara_xz = &a1->bone_data.bones.begin[MOTION_BONE_KL_HARA_XZ]; - bone_data* b_kl_hara_etc = &a1->bone_data.bones.begin[MOTION_BONE_KL_HARA_ETC]; - a1->field_4F8.field_90 = 0.0f; - - mat4 rot_mat; - mat4 mat; - mat4_transpose(&b_n_hara_cp->rot_mat[0], &rot_mat); - mat4_transpose(&b_kg_hara_y->rot_mat[0], &mat); - mat4_mult(&mat, &rot_mat, &rot_mat); - mat4_transpose(&b_kl_hara_xz->rot_mat[0], &mat); - mat4_mult(&mat, &rot_mat, &rot_mat); - mat4_transpose(&b_kl_hara_etc->rot_mat[0], &mat); - mat4_mult(&mat, &rot_mat, &rot_mat); - mat4_transpose(&rot_mat, &b_n_hara_cp->rot_mat[0]); - b_kg_hara_y->rot_mat[0] = mat4_identity; - b_kl_hara_xz->rot_mat[0] = mat4_identity; - b_kl_hara_etc->rot_mat[0] = mat4_identity; - - float_t v8 = a1->field_4F8.field_C0; - float_t v9 = a1->field_4F8.field_C4; - vec3 v10 = a1->field_4F8.field_C8; - a1->field_4F8.field_A8 = b_n_hara_cp->trans; - if (!a1->mot_key_data.skeleton_select) { - if (a2 != v9) { - b_n_hara_cp->trans.x = (b_n_hara_cp->trans.x - v10.x) * v9 + v10.x; - b_n_hara_cp->trans.z = (b_n_hara_cp->trans.z - v10.z) * v9 + v10.z; - } - b_n_hara_cp->trans.y = ((b_n_hara_cp->trans.y - v10.y) * v8) + v10.y; - } - else { - if (a2 != v9) { - v9 /= a2; - b_n_hara_cp->trans.x = (b_n_hara_cp->trans.x - v10.x) * v9 + v10.x; - b_n_hara_cp->trans.z = (b_n_hara_cp->trans.z - v10.z) * v9 + v10.z; - } - - if (a2 != v8) { - v8 /= a2; - b_n_hara_cp->trans.y = (b_n_hara_cp->trans.y - v10.y) * v8 + v10.y; - } - } -} - -static bool sub_1404136B0(motion_blend_mot* a1) { - MotionBlend* v1 = a1->blend; - if (v1) - return v1->__vftable->Field30(v1); - else - return false; -} - -static bool sub_140410250(struc_313* a1, size_t a2) { - return (a1->bitfield.begin[a2 >> 5] & (1 << (a2 & 0x1F))) != 0; -} - -static void sub_140410A40(motion_blend_mot* a1, vector_old_bone_data* a2, vector_old_bone_data* a3) { - MotionBlend* v1 = a1->blend; - if (!v1 || !v1->rot_y) - return; - - v1->__vftable->Field20(v1, a2, a3); - for (bone_data* i = a2->begin; i != a2->end; i++) { - if (!sub_140410250(&a1->field_0.field_8, i->motion_bone_index)) - continue; - - bone_data* v8 = 0; - if (a3) - v8 = &a3->begin[i->motion_bone_index]; - v1->__vftable->Blend(v1, &a2->begin[i->motion_bone_index], v8); - } -} - -static void sub_140410B70(motion_blend_mot* a1, vector_old_bone_data* a2) { - MotionBlend* v1 = a1->blend; - if (!v1 || !v1->rot_y) - return; - - v1->__vftable->Field20(v1, &a1->bone_data.bones, a2); - for (bone_data* i = a1->bone_data.bones.begin; i != a1->bone_data.bones.end; i++) { - if (!sub_140410250(&a1->field_0.field_8, i->motion_bone_index)) - continue; - - bone_data* v6 = 0; - if (a2) - v6 = &a2->begin[i->motion_bone_index]; - v1->__vftable->Blend(a1->blend, &a1->bone_data.bones.begin[i->motion_bone_index], v6); - } -} - -static MotionBlendType sub_1404125C0(motion_blend_mot* a1) { - MotionBlend* v1 = a1->blend; - if (v1 == &a1->cross.base) - return MOTION_BLEND_CROSS; - if (v1 == &a1->freeze.base) - return MOTION_BLEND_FREEZE; - if (v1 == &a1->combine.base.base) - return MOTION_BLEND_COMBINE; - return MOTION_BLEND; -} - -static void sub_140410CB0(mot_blend* a1, vector_old_bone_data* a2) { - MotionBlend* v1 = &a1->blend.base; - if (!v1->rot_y) - return; - - for (bone_data* i = a2->begin; i != a2->end; i++) - if (sub_140410250(&a1->field_0.field_8, i->motion_bone_index)) - v1->__vftable->Blend(&a1->blend.base, &a2->begin[i->motion_bone_index], 0); -} - -static void sub_1404182B0(rob_chara_bone_data* rob_bone_data) { - if (!rob_bone_data->motion_loaded.size) - return; - - list_ptr_motion_blend_mot_node* head = rob_bone_data->motion_loaded.head; - list_ptr_motion_blend_mot_node* v2 = head; - while (v2 != head->next) { - bool v2a = v2 != head; - v2 = v2->prev; - if (sub_1404136B0(v2->value)) - if (v2a) - sub_140410A40(v2->value, &v2->prev->value->bone_data.bones, - &v2->value->bone_data.bones); - } - - list_ptr_motion_blend_mot_node* v5 = head; - while (v5 != head->next) { - bool v5a = v5 != head; - v5 = v5->prev; - if (!sub_1404136B0(v5->value)) - if (v5a) - sub_140410B70(v5->value, &v5->next->value->bone_data.bones); - else if (sub_1404125C0(v5->value) == MOTION_BLEND_FREEZE) - sub_140410B70(v5->value, 0); - } - - motion_blend_mot* v3 = head->next->value; - sub_140410CB0(&rob_bone_data->face, &v3->bone_data.bones); - sub_140410CB0(&rob_bone_data->hand_l, &v3->bone_data.bones); - sub_140410CB0(&rob_bone_data->hand_r, &v3->bone_data.bones); - sub_140410CB0(&rob_bone_data->mouth, &v3->bone_data.bones); - sub_140410CB0(&rob_bone_data->eye, &v3->bone_data.bones); - sub_140410CB0(&rob_bone_data->eyelid, &v3->bone_data.bones); - - bone_data* b_n_hara_cp = &v3->bone_data.bones.begin[MOTION_BONE_N_HARA_CP]; - v3->field_4F8.field_9C = b_n_hara_cp->trans; - if (v3->field_4F8.field_0 & 2) { - v3->field_4F8.field_90 = b_n_hara_cp->trans; - b_n_hara_cp->trans = 0.0f; - } -} - -static void sub_14041B9F0(rob_chara_bone_data* rob_bone_data) { - list_ptr_motion_blend_mot_node* v1 = rob_bone_data->motion_loaded.head; - list_ptr_motion_blend_mot_node* v3 = v1->next; - while (v3 != v1) { - sub_1404146F0(&v3->value->field_0); - v3 = v3->next; - } - sub_1404146F0(&rob_bone_data->face.field_0); - sub_1404146F0(&rob_bone_data->eyelid.field_0); -} - -static void motion_blend_mot_mult_mat(motion_blend_mot* a1, mat4* a2) { - sub_140414900(&a1->field_4F8, a2); - - mat4 mat = a1->field_4F8.mat; - bone_data* v3 = a1->bone_data.bones.begin; - for (bone_data* v4 = a1->bone_data.bones.begin; v4 != a1->bone_data.bones.end; v4++) - bone_data_mult_1(v4, &mat, v3, true); -} - -void rob_chara_bone_data_update(rob_chara_bone_data* rob_bone_data, mat4* a2) { - if (!rob_bone_data->motion_loaded.size) - return; - - list_ptr_motion_blend_mot_node* v4 = rob_bone_data->motion_loaded.head; - list_ptr_motion_blend_mot_node* v5 = v4->next; - while (v5 != v4) { - sub_1404117F0(v5->value); - sub_14040FBF0(v5->value, rob_bone_data->ik_scale.ratio0); - v5 = v5->next; - } - sub_1404182B0(rob_bone_data); - sub_14041B9F0(rob_bone_data); - motion_blend_mot_mult_mat(rob_bone_data->motion_loaded.head->next->value, a2); -} - -static void rob_chara_bone_data_eyes_xrot_adjust(rob_chara_bone_data* rob_bone_data, - struc_241* a2, eyes_adjust* a3) { - float_t v1_neg; - float_t v1_pos; - switch (a3->base_adjust) { - case EYES_BASE_ADJUST_DIRECTION: - default: - v1_neg = a2->field_34 * -(float_t)(1.0 / 3.8); - v1_pos = a2->field_38 * (float_t)(1.0 / 6.0); - break; - case EYES_BASE_ADJUST_CLEARANCE: - v1_neg = a2->field_3C * -(float_t)(1.0 / 3.8); - v1_pos = a2->field_40 * (float_t)(1.0 / 6.0); - break; - case EYES_BASE_ADJUST_OFF: - v1_neg = 1.0f; - v1_pos = 1.0f; - break; - } - - float_t v2_pos = 1.0f; - float_t v2_neg = 1.0f; - if (a3->xrot_adjust) { - v2_neg = a2->field_2C; - v2_pos = a2->field_30; - } - - if (a3->neg >= 0.0f && a3->pos >= 0.0f) { - v2_neg = a3->neg; - v2_pos = a3->pos; - } - - float_t eyes_xrot_adjust_neg = v1_neg * v2_neg; - float_t eyes_xrot_adjust_pos = v1_pos * v2_pos; - for (motion_blend_mot** i = rob_bone_data->motions.begin; i != rob_bone_data->motions.end; i++) { - bone_data* data = (*i)->bone_data.bones.begin; - data[MOTION_BONE_KL_EYE_L].eyes_xrot_adjust_neg = eyes_xrot_adjust_neg; - data[MOTION_BONE_KL_EYE_L].eyes_xrot_adjust_pos = eyes_xrot_adjust_pos; - data[MOTION_BONE_KL_EYE_R].eyes_xrot_adjust_neg = eyes_xrot_adjust_neg; - data[MOTION_BONE_KL_EYE_R].eyes_xrot_adjust_pos = eyes_xrot_adjust_pos; - } -} - -static void rob_chara_bone_data_init_skeleton(rob_chara_bone_data* rob_bone_data, - bone_database_skeleton_type base_skeleton_type, - bone_database_skeleton_type skeleton_type) { - if (rob_bone_data->base_skeleton_type == base_skeleton_type - && rob_bone_data->skeleton_type == skeleton_type) - return; - - bone_database_bone* bones = bone_database_get_bones_by_type(base_skeleton_type); - rob_chara_bone_data_calculate_bones(rob_bone_data, bones); - rob_chara_bone_data_reserve(rob_bone_data); - rob_chara_bone_data_set_mats(rob_bone_data, bones); - uint16_t* parent_indices = bone_database_get_skeleton_parent_indices_by_type(base_skeleton_type); - rob_chara_bone_data_set_parent_mats(rob_bone_data, parent_indices); - rob_bone_data->base_skeleton_type = base_skeleton_type; - rob_bone_data->skeleton_type = skeleton_type; -} - -static bool motion_blend_mot_check(motion_bone_index bone_index) { - return true; -} - -static bool mot_blend_face_check(motion_bone_index bone_index) { - return bone_index >= MOTION_BONE_FACE_ROOT && bone_index <= MOTION_BONE_TL_TOOTH_UPPER_WJ; -} - -static bool mot_blend_hand_l_check(motion_bone_index bone_index) { - return bone_index >= MOTION_BONE_N_HITO_L_EX && bone_index <= MOTION_BONE_NL_OYA_C_L_WJ; -} - -static bool mot_blend_hand_r_check(motion_bone_index bone_index) { - return bone_index >= MOTION_BONE_N_HITO_R_EX && bone_index <= MOTION_BONE_NL_OYA_C_R_WJ; -} - -static bool mot_blend_mouth_check(motion_bone_index bone_index) { - return (bone_index >= MOTION_BONE_N_AGO && bone_index <= MOTION_BONE_TL_TOOTH_UNDER_WJ) - || (bone_index >= MOTION_BONE_N_KUTI_D && bone_index <= MOTION_BONE_TL_KUTI_U_R_WJ) - || (bone_index >= MOTION_BONE_N_TOOTH_UPPER && bone_index <= MOTION_BONE_TL_TOOTH_UPPER_WJ); -} - -static bool mot_blend_eye_check(motion_bone_index bone_index) { - return bone_index >= MOTION_BONE_N_EYE_L && bone_index <= MOTION_BONE_KL_HIGHLIGHT_R_WJ; -} - -static bool mot_blend_eyelid_check(motion_bone_index bone_index) { - return (bone_index >= MOTION_BONE_N_EYE_L && bone_index <= MOTION_BONE_KL_HIGHLIGHT_R_WJ) - || (bone_index >= MOTION_BONE_N_EYELID_L_A && bone_index <= MOTION_BONE_TL_EYELID_R_B_WJ) - || (bone_index >= MOTION_BONE_N_MABU_L_D_A && bone_index <= MOTION_BONE_TL_MABU_R_U_C_WJ); -} - -static void mot_key_data_get_key_set_count_by_bone_database_bones(mot_key_data* a1, bone_database_bone* bones) { - size_t object_bone_count; - size_t motion_bone_count; - size_t total_bone_count; - size_t ik_bone_count; - size_t chain_pos; - bone_database_bones_calculate_count(bones, &object_bone_count, - &motion_bone_count, &total_bone_count, &ik_bone_count, &chain_pos); - mot_key_data_get_key_set_count(a1, motion_bone_count, ik_bone_count); -} - -static void mot_key_data_reserve_key_sets_by_skeleton_type(mot_key_data* a1, - bone_database_skeleton_type type) { - mot_key_data_get_key_set_count_by_bone_database_bones(a1, bone_database_get_bones_by_type(type)); - mot_key_data_reserve_key_sets(a1); - a1->skeleton_type = type; -} - -static void mot_blend_reserve_key_sets(mot_blend* blend, bone_database_skeleton_type type, - bool(*a3)(motion_bone_index), size_t motion_bone_count) { - mot_key_data_reserve_key_sets_by_skeleton_type(&blend->mot_key_data, type); - sub_140413350(&blend->field_0, a3, motion_bone_count); -} - -static void rob_chara_bone_data_init_data(rob_chara_bone_data* rob_bone_data, - bone_database_skeleton_type base_type, - bone_database_skeleton_type type) { - rob_chara_bone_data_init_skeleton(rob_bone_data, base_type, type); - for (motion_blend_mot** i = rob_bone_data->motions.begin; i != rob_bone_data->motions.end; i++) - motion_blend_mot_load_bone_data(*i, rob_bone_data, motion_blend_mot_check); - size_t motion_bone_count = rob_bone_data->motion_bone_count; - mot_blend_reserve_key_sets(&rob_bone_data->face, base_type, mot_blend_face_check, motion_bone_count); - mot_blend_reserve_key_sets(&rob_bone_data->hand_l, base_type, mot_blend_hand_l_check, motion_bone_count); - mot_blend_reserve_key_sets(&rob_bone_data->hand_r, base_type, mot_blend_hand_r_check, motion_bone_count); - mot_blend_reserve_key_sets(&rob_bone_data->mouth, base_type, mot_blend_mouth_check, motion_bone_count); - mot_blend_reserve_key_sets(&rob_bone_data->eye, base_type, mot_blend_eye_check, motion_bone_count); - mot_blend_reserve_key_sets(&rob_bone_data->eyelid, base_type, mot_blend_eyelid_check, motion_bone_count); -} - -static void sub_140548350(struc_525* a1) { - a1->field_0 = 0; - a1->field_4 = 0; -} - -static int32_t* sub_140507140(int32_t* a1) { - *a1 = 0; - return a1; -} - -static void sub_140506C20(struc_523* a1) { - a1->field_0 = 0; - a1->field_1 = 0; - a1->field_4 = 0; - a1->field_8 = 0; - a1->field_C = 0; - a1->field_10 = 0; - a1->field_1C = 0; - a1->field_14 = 0; - a1->field_18 = 0; - a1->field_20 = 0; - a1->field_24 = 0; - a1->field_28 = 0; - a1->field_2C = 0; - a1->field_30 = 0; - a1->field_34 = 0; - a1->field_38 = 0; - a1->field_3C = 0; - a1->field_40 = 0; - a1->field_44 = 0; - a1->field_48 = 0; - a1->field_4C = 0; - a1->field_50 = 0; - - list_ptr_void_node* v2 = a1->field_58.head; - list_ptr_void_node* v3 = v2->next; - v2->next = v2; - a1->field_58.head->prev = a1->field_58.head; - a1->field_58.size = 0; - while (v3 != a1->field_58.head) { - list_ptr_void_node* v4 = v3->next; - operator_delete(v3); - v3 = v4; - } - a1->field_68 = 0; - a1->field_70 = 0; - a1->field_78 = 0; - a1->field_80 = 0; -} - -static void sub_1405071C0(struc_526* a1) { - a1->field_0 = 0; - a1->field_4 = 0; -} - -static void sub_140505C90(struc_264* a1) { - a1->field_0 = 0; - a1->field_4.field_0 = 0; - sub_140548350(&a1->field_4.field_4); - a1->field_4.motion_id = -1; - a1->field_4.field_10 = 0; - a1->field_18.field_0 = 0; - sub_140548350(&a1->field_18.field_4); - a1->field_18.motion_id = -1; - a1->field_18.field_10 = 0; - a1->field_2C.field_0 = 0; - sub_140548350(&a1->field_2C.field_4); - a1->field_2C.motion_id = -1; - a1->field_2C.field_10 = 0; - a1->field_40.field_0 = 0; - sub_140548350(&a1->field_40.field_4); - a1->field_40.motion_id = -1; - a1->field_40.field_10 = 0; - a1->field_54.field_0 = 0; - sub_140548350(&a1->field_54.field_4); - a1->field_54.motion_id = -1; - a1->field_54.field_10 = 0; - a1->field_68 = 0; - a1->field_6C = 0; - a1->field_70 = 0; - a1->field_74 = 0; - a1->field_78 = 0x7FFF; - a1->field_7C = 0; - a1->field_80 = 0; - a1->field_84 = 0; - a1->field_88 = 0; - a1->field_8C = 0; - a1->field_90 = 0; - a1->field_94 = 0; - a1->field_98 = 0; - a1->field_9C = 0; - a1->field_A0 = 0; - a1->field_A4 = 0; - a1->field_A5 = 0; - a1->field_A8 = 0; - sub_140507140(&a1->field_AC); - a1->field_B0 = 0; - a1->field_B4 = 0; - sub_140506C20(&a1->field_B8); - a1->field_140 = 0; - a1->field_144 = 0; - a1->field_145 = 0; - a1->field_146 = 0; - a1->field_147 = 0; - a1->field_148 = 0; - a1->field_14C = 0; - a1->field_150 = 0; - a1->field_152 = 0; - a1->field_154 = 0; - a1->field_158 = 0; - a1->field_15C = 0; - a1->field_160 = 0; - a1->field_164 = 0; - a1->field_168 = -1; - a1->field_16C = 0; - a1->field_170 = 0; - a1->field_174 = 0; - a1->field_178 = 0; - a1->field_17C = 0; - a1->field_180 = 0; - a1->field_184 = 0; - a1->field_188 = 0; - a1->field_18C = 999.0f; - a1->field_190 = 0; - a1->field_194 = 0; - a1->field_198 = 0; - a1->field_19C = 0; - a1->field_1A0 = 0; - a1->field_1A4 = 0; - a1->field_1A8 = 0; - a1->field_1B0 = 0; - a1->field_1B4 = 0; - a1->field_1B8 = 0; - a1->field_1BC = -1; - sub_1405071C0(&a1->field_1C0); - a1->field_1C8 = 0; - a1->field_1C9 = 0; - a1->field_1CA = 0; - a1->field_1CC = 0; - a1->field_1D0 = 0; - a1->field_1D4 = 0; -} - -static void rob_sub_action_reset(RobSubAction* sub_act) { - sub_act->data.field_0 = 0; - sub_act->data.field_8 = 0; - sub_act->data.field_10 = 0; - sub_act->data.field_18 = 0; - sub_act->data.cry.base.__vftable->Field_8(&sub_act->data.cry.base); - sub_act->data.shake_hand.base.__vftable->Field_8(&sub_act->data.shake_hand.base); - sub_act->data.embarrassed.base.__vftable->Field_8(&sub_act->data.embarrassed.base); - sub_act->data.angry.base.__vftable->Field_8(&sub_act->data.angry.base); - sub_act->data.laugh.base.__vftable->Field_8(&sub_act->data.laugh.base); -} - -static int32_t* sub_140507180(int32_t* a1) { - *a1 = 0; - return a1; -} - -static void rob_chara_motion_reset(rob_chara_motion* a1) { - a1->motion_id = -1; - a1->prev_motion_id = -1; - a1->frame_data.frame = 0.0f; - a1->frame_data.prev_frame = 0.0f; - a1->frame_data.last_set_frame = 0.0f; - a1->step_data.frame = 0.0f; - a1->step_data.field_4 = 0.0f; - a1->step_data.step = -1.0f; - a1->step = -1.0f; - a1->field_24 = 0; - a1->field_28 = 0; - a1->field_2C = 0; - sub_140507180(&a1->field_30); - a1->field_34 = 0; - - int32_t* v2 = a1->field_38; - int32_t* v3 = a1->field_B8; - for (int32_t i = 0; i < 32; i++) { - *v2++ = -1; - *v3++ = 0; - } - - a1->field_138 = 1.0f; - a1->field_13C = 1.0f; - a1->field_140 = 0; - a1->field_144 = 0; - a1->field_150.face.base.__vftable->Reset(&a1->field_150.face.base); - a1->field_150.hand_l.base.__vftable->Reset(&a1->field_150.hand_l.base); - a1->field_150.hand_r.base.__vftable->Reset(&a1->field_150.hand_r.base); - a1->field_150.mouth.base.__vftable->Reset(&a1->field_150.mouth.base); - a1->field_150.eye.base.__vftable->Reset(&a1->field_150.eye.base); - a1->field_150.eyelid.base.__vftable->Reset(&a1->field_150.eyelid.base); - a1->field_150.head_object = object_info_null; - a1->field_150.hand_l_object = object_info_null; - a1->field_150.hand_r_object = object_info_null; - a1->field_150.face_object = object_info_null; - a1->field_150.field_1C0 = 0; - a1->field_150.time = 0.0f; - a1->hand_l.base.__vftable->Reset(&a1->hand_l.base); - a1->hand_r.base.__vftable->Reset(&a1->hand_r.base); - a1->hand_l_object = object_info_null; - a1->hand_r_object = object_info_null; - a1->field_3B0.face.base.__vftable->Reset(&a1->field_3B0.face.base); - a1->field_3B0.hand_l.base.__vftable->Reset(&a1->field_3B0.hand_l.base); - a1->field_3B0.hand_r.base.__vftable->Reset(&a1->field_3B0.hand_r.base); - a1->field_3B0.mouth.base.__vftable->Reset(&a1->field_3B0.mouth.base); - a1->field_3B0.eye.base.__vftable->Reset(&a1->field_3B0.eye.base); - a1->field_3B0.eyelid.base.__vftable->Reset(&a1->field_3B0.eyelid.base); - a1->field_3B0.head_object = object_info_null; - a1->field_3B0.hand_l_object = object_info_null; - a1->field_3B0.hand_r_object = object_info_null; - a1->field_3B0.face_object = object_info_null; - a1->field_3B0.field_1C0 = 0; - a1->field_3B0.time = 0.0f; - - rob_chara_data_adjust* parts_adjust = a1->parts_adjust; - rob_chara_data_adjust* parts_adjust_prev = a1->parts_adjust_prev; - for (int32_t i = 0; i < 13; i++) { - parts_adjust->enable = false; - parts_adjust->frame = 0.0f; - parts_adjust->transition_frame = 0.0f; - parts_adjust->curr_external_force = 0.0f; - parts_adjust->curr_force = 1.0f; - parts_adjust->curr_strength = 1.0f; - parts_adjust->motion_id = -1; - parts_adjust->set_frame = 0; - parts_adjust->force_duration = 0.0f; - parts_adjust->type = 6; - parts_adjust->cycle_type = 0; - parts_adjust->ignore_gravity = false; - parts_adjust->external_force = 0.0f; - parts_adjust->external_force_cycle_strength = 0.0f; - parts_adjust->external_force_cycle = 0.0f; - parts_adjust->cycle = 1.0f; - parts_adjust->phase = 0.0f; - parts_adjust->force = 1.0f; - parts_adjust->strength = 1.0f; - parts_adjust->strength_transition = 0.0f; - parts_adjust_prev->enable = false; - parts_adjust_prev->frame = 0.0f; - parts_adjust_prev->transition_frame = 0.0f; - parts_adjust_prev->curr_external_force = 0.0f; - parts_adjust_prev->curr_force = 1.0f; - parts_adjust_prev->curr_strength = 1.0f; - parts_adjust_prev->motion_id = -1; - parts_adjust_prev->set_frame = 0; - parts_adjust_prev->force_duration = 0.0f; - parts_adjust_prev->type = 6; - parts_adjust_prev->cycle_type = 0; - parts_adjust_prev->ignore_gravity = false; - parts_adjust_prev->external_force = 0.0f; - parts_adjust_prev->external_force_cycle_strength = 0.0f; - parts_adjust_prev->external_force_cycle = 0.0f; - parts_adjust_prev->cycle = 1.0f; - parts_adjust_prev->phase = 0.0f; - parts_adjust_prev->force = 1.0f; - parts_adjust_prev->strength = 1.0f; - parts_adjust_prev->strength_transition = 0.0f; - parts_adjust++; - parts_adjust_prev++; - } - - rob_chara_data_adjust* v7 = &a1->adjust_global; - rob_chara_data_arm_adjust* v8 = a1->arm_adjust; - rob_chara_data_hand_adjust* hand_adjust = a1->hand_adjust; - rob_chara_data_hand_adjust* hand_adjust_prev = a1->hand_adjust_prev; - for (int32_t i = 0; i < 2; i++) { - v7->enable = false; - v7->frame = 0.0f; - v7->transition_frame = 0.0f; - v7->curr_external_force = 0.0f; - v7->curr_force = 1.0f; - v7->curr_strength = 1.0f; - v7->motion_id = -1; - v7->set_frame = 0; - v7->force_duration = 0.0f; - v7->type = 6; - v7->cycle_type = 0; - v7->ignore_gravity = false; - v7->external_force = 0.0f; - v7->external_force_cycle_strength = 0.0f; - v7->external_force_cycle = 0.0f; - v7->cycle = 1.0f; - v7->phase = 0.0f; - v7->force = 1.0f; - v7->strength = 1.0f; - v7->strength_transition = 0.0f; - v8->enable = 0; - v8->value = 0.0f; - v8->prev_value = 0.0f; - v8->next_value = 0.0f; - v8->duration = 0.0f; - v8->frame = 0.0f; - hand_adjust->enable = false; - hand_adjust->scale_select = 0; - hand_adjust->type = ROB_CHARA_DATA_HAND_ADJUST_NONE; - hand_adjust->current_scale = 1.0f; - hand_adjust->scale = 1.0f; - hand_adjust->duration = 0.0f; - hand_adjust->current_time = 0.0f; - hand_adjust->rotation_blend = 1.0f; - hand_adjust->scale_blend = 1.0f; - hand_adjust->enable_scale = false; - hand_adjust->disable_x = false; - hand_adjust->disable_y = false; - hand_adjust->disable_z = false; - hand_adjust->offset = 0.0f; - hand_adjust->field_30 = 0.0f; - hand_adjust->arm_length = 0; - hand_adjust_prev->enable = false; - hand_adjust_prev->scale_select = 0; - hand_adjust_prev->type = ROB_CHARA_DATA_HAND_ADJUST_NONE; - hand_adjust_prev->current_scale = 1.0f; - hand_adjust_prev->scale = 1.0f; - hand_adjust_prev->duration = 0.0f; - hand_adjust_prev->current_time = 0.0f; - hand_adjust_prev->rotation_blend = 1.0f; - hand_adjust_prev->scale_blend = 1.0f; - hand_adjust_prev->enable_scale = false; - hand_adjust_prev->disable_x = false; - hand_adjust_prev->disable_y = false; - hand_adjust_prev->disable_z = false; - hand_adjust_prev->offset = 0.0f; - hand_adjust_prev->field_30 = 0.0f; - hand_adjust_prev->arm_length = 0; - v7++; - v8++; - hand_adjust++; - hand_adjust_prev++; - } -} - -static struc_228* sub_14053A660(struc_228* a1) { - a1->field_0 = 0; - a1->field_4 = 0; - a1->field_8 = 0; - a1->field_C = 0; - return a1; -} - -static void sub_140539750(struc_223* a1) { - void(*sub_140537110)(struc_223 * a1) - = (void(*)(struc_223*))0x0000000140537110; - void(*sub_1405335C0)(struc_223 * a1) - = (void(*)(struc_223*))0x00000001405335C0; - - sub_14053A660(&a1->field_0.field_20); - sub_14053A660(&a1->field_0.field_10); - sub_140537110(a1); - sub_1405335C0(a1); - a1->field_7A0 = 0; - a1->motion_set_id = -1; -} - -static void rob_chara_data_reset(rob_chara_data* a1) { - void(*rob_chara_data_miku_rot_reset)(rob_chara_data_miku_rot * a1) - = (void(*)(rob_chara_data_miku_rot*))0x0000000140506A20; - void(*rob_chara_data_adjuct_reset)(rob_chara_adjust_data * a1) - = (void(*)(rob_chara_adjust_data*))0x0000000140506AE0; - void(*sub_140505FB0)(struc_209 * a1) - = (void(*)(struc_209*))0x0000000140505FB0; - - a1->field_0 = 0; - sub_140505C90(&a1->field_8); - rob_sub_action_reset(&a1->rob_sub_action); - rob_chara_motion_reset(&a1->motion); - sub_140539750(&a1->field_1588); - rob_chara_data_miku_rot_reset(&a1->miku_rot); - rob_chara_data_adjuct_reset(&a1->adjust_data); - sub_140505FB0(&a1->field_1E68); - a1->field_3D90 = 0.0f; - a1->field_3D94 = 0; - a1->field_3D98 = 0; - a1->field_3D9A = 0; - a1->field_3D9C = 0; - a1->field_3D9D = 0; - a1->field_3DA0 = 0; - a1->field_3DA4 = 0; - a1->field_3DA8 = 0; - a1->field_3DB0 = 0; - a1->field_3DB8 = 0; - a1->field_3DBC = 0; - a1->field_3DC0 = 0; - a1->field_3DC4 = 0; - a1->field_3DC8 = 0; - a1->field_3DCC = 0; - a1->field_3DD0 = 0; - a1->field_3DD8 = 0; - a1->field_3DD4 = 0.0f; - a1->field_3DDC = 0.0f; - a1->field_3DE0 = 0; -} - -static void bone_data_parent_reset(bone_data_parent* a1) { - a1->rob_bone_data = 0; - a1->field_8 = false; - a1->field_9 = false; - a1->motion_bone_count = 0; - a1->global_trans = 0.0f; - a1->global_rotation = 0.0f; - a1->ik_bone_count = 0; - a1->chain_pos = 0; - a1->bone_key_set_count = 0; - a1->global_key_set_count = 0; - a1->field_78 = 0; - a1->rot_y = 0; - a1->bones.end = a1->bones.begin; - a1->bone_indices.end = a1->bone_indices.begin; -} - -static void sub_140413470(struc_308* a1) { - a1->field_0 = 0; - a1->field_8 = 0; - a1->mat = mat4_identity; - a1->field_4C = mat4_identity; - a1->field_8C = false; - a1->rot_y = 0.0f; - a1->prev_rot_y = 0.0f; - a1->field_90 = 1.0f; - a1->field_9C = 1.0f; - a1->field_A8 = 1.0f; - a1->field_BC = 0; - a1->field_BD = 0; - a1->field_C0 = 1.0f; - a1->field_C4 = 1.0f; - a1->field_C8 = 0.0f; -} - -static void motion_blend_mot_reset(motion_blend_mot* a1) { - bone_data_parent_reset(&a1->bone_data); - a1->mot_key_data.key_sets_ready = 0; - a1->mot_key_data.skeleton_type = BONE_DATABASE_SKELETON_NONE; - a1->mot_key_data.frame = -1.0f; - a1->mot_key_data.key_set_count = 0; - a1->mot_key_data.mot_data = 0; - a1->mot_key_data.skeleton_select = 0; - a1->mot_key_data.field_68.field_0 = 0; - a1->mot_key_data.field_68.field_4 = 0.0f; - a1->mot_key_data.key_set.end = a1->mot_key_data.key_set.begin; - a1->mot_key_data.key_set_data.end = a1->mot_key_data.key_set_data.begin; - a1->mot_key_data.motion_id = -1; - a1->mot_play_data.frame_data.frame = 0.0f; - a1->mot_play_data.frame_data.step = 1.0f; - a1->mot_play_data.frame_data.step_prev = 1.0f; - a1->mot_play_data.frame_data.frame_count = 0.0f; - a1->mot_play_data.frame_data.last_frame = -1.0f; - a1->mot_play_data.frame_data.field_14 = -1.0f; - a1->mot_play_data.frame_data.field_18 = -1; - a1->mot_play_data.frame_data.field_1C = -1; - a1->mot_play_data.frame_data.field_20 = 0.0f; - a1->mot_play_data.frame_data.field_24 = -1.0f; - a1->mot_play_data.frame_data.field_28 = 0; - a1->mot_play_data.frame_data.field_2C = -1; - a1->mot_play_data.field_30 = 0; - a1->mot_play_data.field_34 = 0; - a1->mot_play_data.field_38 = 0.0f; - a1->mot_play_data.field_40 = 0; - a1->mot_play_data.field_48 = 0; - sub_140413470(&a1->field_4F8); - a1->field_0.bone_check_func = 0; - sub_1404119A0(&a1->field_0.field_8); - a1->field_0.motion_bone_count = 0; - a1->cross.base.__vftable->Reset(&a1->cross.base); - a1->freeze.base.__vftable->Reset(&a1->freeze.base); - a1->combine.base.base.__vftable->Reset(&a1->combine.base.base); - a1->blend = 0; -} - -static void mot_blend_reset(mot_blend* a1) { - a1->mot_key_data.key_sets_ready = 0; - a1->mot_key_data.skeleton_type = BONE_DATABASE_SKELETON_NONE; - a1->mot_key_data.frame = -1.0f; - a1->mot_key_data.key_set_count = 0; - a1->mot_key_data.mot_data = 0; - a1->mot_key_data.skeleton_select = 0; - a1->mot_key_data.field_68.field_0 = 0; - a1->mot_key_data.field_68.field_4 = 0.0f; - a1->mot_key_data.key_set.end = a1->mot_key_data.key_set.begin; - a1->mot_key_data.key_set_data.end = a1->mot_key_data.key_set_data.begin; - a1->mot_key_data.motion_id = -1; - a1->mot_play_data.frame_data.frame = 0.0f; - a1->mot_play_data.frame_data.step = 1.0f; - a1->mot_play_data.frame_data.step_prev = 1.0f; - a1->mot_play_data.frame_data.frame_count = 0.0f; - a1->mot_play_data.frame_data.last_frame = -1.0f; - a1->mot_play_data.frame_data.field_14 = -1.0f; - a1->mot_play_data.frame_data.field_18 = -1; - a1->mot_play_data.frame_data.field_1C = -1; - a1->mot_play_data.frame_data.field_20 = 0.0f; - a1->mot_play_data.frame_data.field_24 = -1.0f; - a1->mot_play_data.frame_data.field_28 = 0; - a1->mot_play_data.frame_data.field_2C = -1; - a1->mot_play_data.field_30 = 0; - a1->mot_play_data.field_34 = 0; - a1->mot_play_data.field_38 = 0; - a1->mot_play_data.field_40 = 0; - a1->mot_play_data.field_48 = 0; - a1->blend.base.__vftable->Reset(&a1->blend.base); - a1->field_0.bone_check_func = 0; - sub_1404119A0(&a1->field_0.field_8); - a1->field_0.motion_bone_count = 0; - a1->field_30 = 0; -} - -static void sub_1404198D0(struc_258* a1) { - a1->field_0 = 0; - a1->field_8 = mat4_identity; - a1->field_48.field_0 = 0.0f; - a1->field_48.field_4 = 0.0f; - a1->field_48.field_8 = 0.0f; - a1->field_48.field_C = 0.0f; - a1->field_48.field_10 = 0.0f; - a1->field_48.field_14 = 0.0f; - a1->field_48.field_18 = 0.0f; - a1->field_48.field_1C = 0.0f; - a1->field_48.field_20 = 0.0f; - a1->field_48.field_24 = 0.0f; - a1->field_48.field_28 = 0.0f; - a1->field_48.field_2C = 1.0f; - a1->field_48.field_30 = 1.0f; - a1->field_48.field_34 = -3.8f; - a1->field_48.field_38 = 6.0f; - a1->field_48.field_3C = -3.8f; - a1->field_48.field_40 = 6.0f; - a1->field_8C = 0; - a1->field_8D = 0; - a1->field_8E = 0; - a1->field_8F = 0; - a1->field_90 = 0; - a1->field_91 = 0; - a1->field_94 = 0.0f; - a1->field_98 = 1.0f; - a1->field_9C = 1.0f; - a1->field_A0 = 0; - a1->field_A4 = 0; - a1->field_A8 = 0; - a1->field_AC = 0; - a1->field_B0 = 0.0f; - a1->field_B4 = 0.0f; - a1->field_C0 = 0.0f; - a1->field_CC = mat4_identity; - a1->field_10C = mat4_identity; - a1->field_14C = 0; - a1->field_150 = 0; - a1->field_158 = 0.0f; - a1->field_15C.field_0 = 0; - a1->field_15C.field_4 = 0; - a1->field_15C.field_8 = 0; - a1->field_15C.field_C = 0; - a1->field_15C.field_10 = 0; - a1->field_15C.field_14 = 0; - a1->field_15C.field_18 = 0; - a1->field_15C.field_1C = 0; - a1->field_15C.field_20 = 0; - a1->field_15C.field_24 = 0; - a1->field_184 = 0; - a1->field_188 = 0; - a1->field_18C = 0; - a1->field_190 = 0; - a1->field_194 = 0; - a1->field_195 = 0; - a1->field_198 = 0; - a1->field_19C = 0; - a1->field_1A0 = 0; - a1->field_1A4 = 0; - a1->field_1A8 = 0; - a1->field_1AC = 0; - a1->field_1B0 = 0; - a1->field_1B4 = 0; - a1->field_1B8 = 0; - a1->field_1C8 = 0; - a1->field_1BC = 1.0f; - a1->field_1C4 = 0; -} - -static void sub_1403FAEF0(struc_312* a1) { - a1->field_0.field_0 = 0; - a1->field_0.field_4 = 0; - a1->field_0.field_8 = 0; - a1->field_0.field_C = 0; - a1->field_0.field_10 = 0; - a1->field_0.field_14 = 0; - a1->field_0.field_18 = 0; - a1->field_0.field_1C = 0.0f; - a1->field_0.field_28 = 0; - a1->field_2C.field_0 = 0; - a1->field_2C.field_4 = 0; - a1->field_2C.field_8 = 0; - a1->field_2C.field_C = 0; - a1->field_2C.field_10 = 0; - a1->field_2C.field_14 = 0; - a1->field_2C.field_18 = 0; - a1->field_2C.field_1C = 0.0f; - a1->field_2C.field_28 = 0; - a1->field_58 = 0; - a1->field_8C = 1.0f; - a1->bones = 0; - a1->step = 1.0f; - a1->field_5C = 0.0f; - a1->field_68 = 0.0f; - a1->field_74 = 0.0f; - a1->field_80 = 0.0f; -} - -static void rob_chara_bone_data_reset(rob_chara_bone_data* rob_bone_data) { - void(*rob_chara_bone_data_motion_blend_mot_list_free)(rob_chara_bone_data * rob_bone_data, size_t a2) - = (void(*)(rob_chara_bone_data*, size_t))0x0000000140418E80; - void(*rob_chara_bone_data_motion_blend_mot_list_init)(rob_chara_bone_data * rob_bone_data) - = (void(*)(rob_chara_bone_data*))0x000000014041A9E0; - void(*rob_chara_bone_data_motion_blend_mot_free)(rob_chara_bone_data * rob_bone_data) - = (void(*)(rob_chara_bone_data*))0x0000000140419000; - void(*rob_chara_bone_data_motion_blend_mot_init)(rob_chara_bone_data * rob_bone_data) - = (void(*)(rob_chara_bone_data*))0x000000014041A1A0; - - rob_bone_data->field_0 = 0; - rob_bone_data->field_1 = 0; - rob_bone_data->object_bone_count = 0; - rob_bone_data->motion_bone_count = 0; - rob_bone_data->total_bone_count = 0; - rob_bone_data->ik_bone_count = 0; - rob_bone_data->chain_pos = 0; - rob_bone_data->mats.end = rob_bone_data->mats.begin; - rob_bone_data->mats2.end = rob_bone_data->mats2.begin; - rob_bone_data->nodes.end = rob_bone_data->nodes.begin; - rob_bone_data->base_skeleton_type = BONE_DATABASE_SKELETON_NONE; - rob_bone_data->skeleton_type = BONE_DATABASE_SKELETON_NONE; - - if (rob_bone_data->motion_loaded.size && vector_old_length(rob_bone_data->motions) == 3) { - rob_chara_bone_data_motion_blend_mot_list_free(rob_bone_data, 0); - for (motion_blend_mot** i = rob_bone_data->motions.begin; i != rob_bone_data->motions.end; i++) - motion_blend_mot_reset(*i); - rob_chara_bone_data_motion_blend_mot_list_init(rob_bone_data); - } - else { - rob_chara_bone_data_motion_blend_mot_free(rob_bone_data); - rob_chara_bone_data_motion_blend_mot_init(rob_bone_data); - } - - mot_blend_reset(&rob_bone_data->face); - mot_blend_reset(&rob_bone_data->hand_l); - mot_blend_reset(&rob_bone_data->hand_r); - mot_blend_reset(&rob_bone_data->mouth); - mot_blend_reset(&rob_bone_data->eye); - mot_blend_reset(&rob_bone_data->eyelid); - rob_bone_data->disable_eye_motion = 0; - rob_bone_data->ik_scale.ratio0 = 1.0f; - rob_bone_data->ik_scale.ratio1 = 1.0f; - rob_bone_data->ik_scale.ratio2 = 1.0f; - rob_bone_data->ik_scale.ratio3 = 1.0f; - rob_bone_data->field_76C = 0.0f; - rob_bone_data->field_778 = 0.0f; - sub_1404198D0(&rob_bone_data->field_788); - sub_1403FAEF0(&rob_bone_data->field_958); -} - -static skeleton_rotation_offset* skeleton_rotation_offset_array_get(bone_database_skeleton_type type) { - skeleton_rotation_offset** skeleton_rotation_offset_array_ptr - = (skeleton_rotation_offset**)0x0000000140A01560; - if (type >= BONE_DATABASE_SKELETON_COMMON && type <= BONE_DATABASE_SKELETON_TETO) - return skeleton_rotation_offset_array_ptr[type]; - else - return 0; -} - -static vec3* bone_data_set_key_data(bone_data* data, vec3* keyframe_data, - bone_database_skeleton_type skeleton_type, bool get_data, bool reverse_x) { - bone_database_bone_type type = data->type; - if (type == BONE_DATABASE_BONE_POSITION_ROTATION) { - if (get_data) { - data->trans = *keyframe_data; - if (reverse_x) - data->trans.x = -data->trans.x; - } - keyframe_data++; - } - else if (type >= BONE_DATABASE_BONE_HEAD_IK_ROTATION - && type <= BONE_DATABASE_BONE_LEGS_IK_ROTATION) { - if (get_data) { - data->ik_target = *keyframe_data; - if (reverse_x) - data->ik_target.x = -data->ik_target.x; - } - keyframe_data++; - } - - if (get_data) { - if (type == BONE_DATABASE_BONE_TYPE_1 || type == BONE_DATABASE_BONE_POSITION) { - data->trans = *keyframe_data; - if (reverse_x) - data->trans.x = -data->trans.x; - } - else if (!data->flags) { - data->rotation = *keyframe_data; - if (reverse_x) { - skeleton_rotation_offset* rot_off = skeleton_rotation_offset_array_get(skeleton_type); - size_t index = data->motion_bone_index; - if (rot_off[index].x) - data->rotation.x = rot_off[index].rotation.x - data->rotation.x; - if (rot_off[index].y) - data->rotation.y = rot_off[index].rotation.y - data->rotation.y; - if (rot_off[index].z) - data->rotation.z = rot_off[index].rotation.z - data->rotation.z; - } - } - } - keyframe_data++; - return keyframe_data; -} - -static void mot_key_frame_interpolate(mot* a1, float_t frame, float_t* value, - mot_key_set* a4, uint32_t key_set_count, struc_369* a6) { - if (a6->field_0 == 4) { - int32_t frame_int = (int32_t)frame; - if (frame != -0.0f && (float_t)frame_int != frame) - frame = (float_t)(frame < 0.0f ? frame_int - 1 : frame_int); - } - - if (!key_set_count) - return; - - for (uint32_t i = key_set_count; i; i--, a4++, value++) { - mot_key_set_type type = a4->type; - float_t* values = a4->values; - if (type == MOT_KEY_SET_NONE) { - *value = 0.0f; - continue; - } - else if (type == MOT_KEY_SET_STATIC) { - *value = values[0]; - continue; - } - else if (type != MOT_KEY_SET_HERMITE && type != MOT_KEY_SET_HERMITE_TANGENT) { - *value = 0.0f; - continue; - } - - int32_t current_key = a4->current_key; - int32_t keys_count = a4->keys_count; - uint16_t* frames = a4->frames; - int32_t frame_int = (int32_t)frame; - size_t key_index; - if (current_key > keys_count - || (frame_int > a4->last_key + frames[current_key]) - || current_key > 0 && frame_int < frames[current_key - 1]) { - uint16_t* key = a4->frames; - size_t length = keys_count; - size_t temp; - while (length > 0) - if (frame_int < key[temp = length / 2]) - length /= 2; - else { - key += temp + 1; - length -= temp + 1; - } - key_index = key - frames; - } - else { - uint16_t* key = &frames[current_key]; - for (uint16_t* v17 = &frames[keys_count]; key != v17; key++) - if (frame_int < *key) - break; - key_index = key - frames; - } - - bool found = false; - if (key_index < keys_count) - for (; key_index < keys_count; key_index++) - if ((float_t)frames[key_index] >= frame) { - found = true; - break; - } - - if (found) { - float_t next_frame = (float_t)frames[key_index]; - if (fabsf(next_frame - frame) > 0.000001f && key_index > 0) { - float_t curr_frame = (float_t)frames[key_index - 1]; - float_t t = (frame - curr_frame) / (next_frame - curr_frame); - if (type == MOT_KEY_SET_HERMITE) { - values += key_index - 1; - float_t p1 = values[0]; - float_t p2 = values[1]; - *value = (t * 2.0f - 3.0f) * (t * t) * (p1 - p2) + p1; - } - else { - values += key_index * 2 - 2; - float_t p1 = values[0]; - float_t p2 = values[2]; - float_t t1 = values[1]; - float_t t2 = values[3]; - *value = ((t - 1.0f) * t1 + t * t2) * (t - 1.0f) * (frame - curr_frame) - + (t * 2.0f - 3.0f) * (t * t) * (p1 - p2) + p1; - } - a4->current_key = (int32_t)key_index; - continue; - } - } - - key_index--; - if (type == MOT_KEY_SET_HERMITE) - *value = values[key_index]; - else - *value = values[2 * key_index]; - a4->current_key = (int32_t)key_index; - } -} - -static void mot_key_data_interpolate(mot_key_data* a1, float_t frame, - uint32_t key_set_offset, uint32_t key_set_count) { - mot_key_frame_interpolate(&a1->mot, frame, - &a1->key_set_data.begin[key_set_offset], - &a1->key_set.begin[key_set_offset], key_set_count, &a1->field_68); -} - -static bool motion_blend_mot_interpolate_get_reverse(int32_t* a1) { - return *a1 & 0x01 && !(*a1 & 0x08) || !(*a1 & 0x01) && *a1 & 0x08; -} - -static void sub_140415430(motion_blend_mot* a1) { - float_t rot_y = a1->bone_data.rot_y; - vec3& global_trans = a1->bone_data.global_trans; - vec3& global_rotation = a1->bone_data.global_rotation; - mat4 mat; - mat4_rotate_y(rot_y, &mat); - mat4_translate_mult(&mat, &global_trans, &mat); - mat4_rotate_mult(&mat, &global_rotation, &mat); - for (bone_data* i = a1->bone_data.bones.begin; i != a1->bone_data.bones.end; i++) - switch (i->type) { - case BONE_DATABASE_BONE_TYPE_1: - mat4_mult_vec3_trans(&mat, &i->trans, &i->trans); - break; - case BONE_DATABASE_BONE_POSITION_ROTATION: { - mat4 rot_mat; - mat4_clear_trans(&mat, &rot_mat); - mat4_transpose(&rot_mat, &i->rot_mat[0]); - mat4_mult_vec3_trans(&mat, &i->trans, &i->trans); - } break; - case BONE_DATABASE_BONE_HEAD_IK_ROTATION: - case BONE_DATABASE_BONE_ARM_IK_ROTATION: - case BONE_DATABASE_BONE_LEGS_IK_ROTATION: - mat4_mult_vec3_trans(&mat, &i->ik_target, &i->ik_target); - break; - } -} - -static void motion_blend_mot_interpolate(motion_blend_mot* a1) { - vec3* keyframe_data = (vec3*)a1->mot_key_data.key_set_data.begin; - bool reverse = motion_blend_mot_interpolate_get_reverse(&a1->field_4F8.field_0); - float_t frame = a1->mot_play_data.frame_data.frame; - - bone_database_skeleton_type skeleton_type = a1->bone_data.rob_bone_data->base_skeleton_type; - bone_data* bones_data = a1->bone_data.bones.begin; - for (uint16_t* i = a1->bone_data.bone_indices.begin; i != a1->bone_data.bone_indices.end; i++) { - bone_data* data = &bones_data[*i]; - bool get_data = sub_140410250(&a1->field_0.field_8, data->motion_bone_index); - if (reverse && data->mirror != 255) - data = &bones_data[data->mirror]; - - if (get_data && frame != data->frame) { - mot_key_data_interpolate(&a1->mot_key_data, frame, data->key_set_offset, data->key_set_count); - data->frame = frame; - } - - keyframe_data = bone_data_set_key_data(data, keyframe_data, skeleton_type, get_data, reverse); - } - - uint32_t bone_key_set_count = a1->bone_data.bone_key_set_count; - if (frame != a1->mot_key_data.frame) { - mot_key_data_interpolate(&a1->mot_key_data, frame, - bone_key_set_count, a1->bone_data.global_key_set_count); - a1->mot_key_data.frame = frame; - } - - keyframe_data = (vec3*)&a1->mot_key_data.key_set_data.begin[bone_key_set_count]; - vec3 global_trans = keyframe_data[0]; - vec3 global_rotation = keyframe_data[1]; - if (reverse) - vec3_negate(global_trans, global_trans); - a1->bone_data.global_trans = global_trans; - a1->bone_data.global_rotation = global_rotation; - - sub_140415430(a1); -} - -static void mot_blend_interpolate(mot_blend* a1, vector_old_bone_data* bones, - vector_old_uint16_t* bone_indices, bone_database_skeleton_type skeleton_type) { - if (!a1->mot_key_data.key_sets_ready || !a1->mot_key_data.mot_data || a1->field_30) - return; - - float_t frame = a1->mot_play_data.frame_data.frame; - vec3* keyframe_data = (vec3*)a1->mot_key_data.key_set_data.begin; - bone_data* bones_data = bones->begin; - for (uint16_t* i = bone_indices->begin; i != bone_indices->end; i++) { - bone_data* data = &bones_data[*i]; - bool get_data = sub_140410250(&a1->field_0.field_8, data->motion_bone_index); - if (get_data) { - mot_key_data_interpolate(&a1->mot_key_data, frame, data->key_set_offset, data->key_set_count); - data->frame = frame; - } - keyframe_data = bone_data_set_key_data(data, keyframe_data, skeleton_type, get_data, 0); - } -} - -static void sub_1401EB1D0(bone_data* a1, int32_t a2) { - if (!a1->flags) - return; - - if (a1->type >= BONE_DATABASE_BONE_TYPE_1 && a1->type <= BONE_DATABASE_BONE_POSITION_ROTATION) - a1->trans_prev[a2] = a1->trans; - - switch (a1->type) { - case BONE_DATABASE_BONE_ARM_IK_ROTATION: - case BONE_DATABASE_BONE_LEGS_IK_ROTATION: - a1->rot_mat_prev[2][a2] = a1->rot_mat[2]; - case BONE_DATABASE_BONE_HEAD_IK_ROTATION: - a1->rot_mat_prev[1][a2] = a1->rot_mat[1]; - case BONE_DATABASE_BONE_ROTATION: - case BONE_DATABASE_BONE_TYPE_1: - case BONE_DATABASE_BONE_POSITION: - case BONE_DATABASE_BONE_POSITION_ROTATION: - default: - a1->rot_mat_prev[0][a2] = a1->rot_mat[0]; - break; - } -} - -static void sub_14041B4E0(struc_313* a1) { - uint32_t* bitfield = a1->bitfield.begin; - for (uint32_t i = MOTION_BONE_FACE_ROOT; - i <= MOTION_BONE_TL_TOOTH_UPPER_WJ; i++) - bitfield[i >> 5] &= ~(1 << (i & 0x1F)); -} - -static void sub_14041B580(struc_313* a1) { - uint32_t* bitfield = a1->bitfield.begin; - for (uint32_t i = MOTION_BONE_FACE_ROOT; - i <= MOTION_BONE_TL_TOOTH_UPPER_WJ; i++) - bitfield[i >> 5] &= ~(1 << (i & 0x1F)); - - for (uint32_t i = MOTION_BONE_N_EYE_L; - i <= MOTION_BONE_KL_HIGHLIGHT_R_WJ; i++) - bitfield[i >> 5] |= 1 << (i & 0x1F); -} - -static void sub_14041B800(struc_313* a1) { - uint32_t* bitfield = a1->bitfield.begin; - for (uint32_t i = MOTION_BONE_N_KUTI_D; - i <= MOTION_BONE_TL_KUTI_U_R_WJ; i++) - bitfield[i >> 5] &= ~(1 << (i & 0x1F)); - - for (uint32_t i = MOTION_BONE_N_AGO; - i <= MOTION_BONE_TL_TOOTH_UNDER_WJ; i++) - bitfield[i >> 5] &= ~(1 << (i & 0x1F)); - - for (uint32_t i = MOTION_BONE_N_TOOTH_UPPER; - i <= MOTION_BONE_TL_TOOTH_UPPER_WJ; i++) - bitfield[i >> 5] &= ~(1 << (i & 0x1F)); -} - -static void sub_14041B440(struc_313* a1) { - uint32_t* bitfield = a1->bitfield.begin; - for (uint32_t i = MOTION_BONE_N_EYE_L; - i <= MOTION_BONE_KL_HIGHLIGHT_R_WJ; i++) - bitfield[i >> 5] &= ~(1 << (i & 0x1F)); -} - -static void sub_14041B1C0(struc_313* a1) { - uint32_t* bitfield = a1->bitfield.begin; - for (uint32_t i = MOTION_BONE_N_MABU_L_D_A; - i <= MOTION_BONE_TL_MABU_R_U_C_WJ; i++) - bitfield[i >> 5] &= ~(1 << (i & 0x1F)); - - for (uint32_t i = MOTION_BONE_N_EYELID_L_A; - i <= MOTION_BONE_TL_EYELID_R_B_WJ; i++) - bitfield[i >> 5] &= ~(1 << (i & 0x1F)); -} - -static void sub_14041B6B0(struc_313* a1) { - uint32_t* bitfield = a1->bitfield.begin; - for (uint32_t i = MOTION_BONE_N_HITO_L_EX; - i <= MOTION_BONE_NL_OYA_C_L_WJ; i++) - bitfield[i >> 5] &= ~(1 << (i & 0x1F)); -} - -static void sub_14041B750(struc_313* a1) { - uint32_t* bitfield = a1->bitfield.begin; - for (uint32_t i = MOTION_BONE_N_HITO_R_EX; - i <= MOTION_BONE_NL_OYA_C_R_WJ; i++) - bitfield[i >> 5] &= ~(1 << (i & 0x1F)); -} - -static void sub_14041B300(struc_313* a1) { - uint32_t* bitfield = a1->bitfield.begin; - for (uint32_t i = MOTION_BONE_N_MABU_L_D_A; - i <= MOTION_BONE_TL_MABU_R_U_C_WJ; i++) - bitfield[i >> 5] &= ~(1 << (i & 0x1F)); - - for (uint32_t i = MOTION_BONE_N_EYELID_L_A; - i <= MOTION_BONE_TL_EYELID_R_B_WJ; i++) - bitfield[i >> 5] &= ~(1 << (i & 0x1F)); -} - -static void sub_140413EA0(struc_240* a1, void(*a2)(struc_313*)) { - a2(&a1->field_8); -} - -static void sub_14041AD90(rob_chara_bone_data* a1) { - if (a1->mouth.mot_key_data.key_sets_ready && a1->mouth.mot_key_data.mot_data && !a1->mouth.field_30) - sub_140413EA0(&a1->face.field_0, sub_14041B800); - if (a1->eyelid.mot_key_data.key_sets_ready && a1->eyelid.mot_key_data.mot_data && !a1->eyelid.field_30) - sub_140413EA0(&a1->face.field_0, sub_14041B1C0); - else if (a1->eye.mot_key_data.key_sets_ready && a1->eye.mot_key_data.mot_data && !a1->eye.field_30 - || a1->disable_eye_motion) - sub_140413EA0(&a1->face.field_0, sub_14041B440); -} - -static void sub_14041AD50(rob_chara_bone_data* a1) { - if (a1->eye.mot_key_data.key_sets_ready && a1->eye.mot_key_data.mot_data && !a1->eye.field_30 - || a1->disable_eye_motion) - sub_140413EA0(&a1->eyelid.field_0, sub_14041B440); -} - -static void sub_140412F20(mot_blend* a1, vector_old_bone_data* a2) { - uint32_t* bitfield = a1->field_0.field_8.bitfield.begin; - for (bone_data* i = a2->begin; i != a2->end; ++i) { - motion_bone_index v5 = i->motion_bone_index; - if (bitfield[v5 >> 5] & (1 << (v5 & 0x1F))) - sub_1401EB1D0(i, 0); - } -} - -static mot_data* mot_key_data_load_file(mot_key_data* a1, int32_t motion_id) { - mot_data* (*sub_140403C90)(int32_t a1) - = (mot_data * (*)(int32_t))0x0000000140403C90; - bool(*mot_load_file)(mot * a1, mot_key_set_file * a2) - = (bool(*)(mot*, mot_key_set_file*))0x000000014036F7B0; - - mot_data* v4; - if (a1->motion_id == motion_id) - v4 = a1->mot_data; - else { - v4 = sub_140403C90(motion_id); - a1->mot_data = v4; - if (v4) { - a1->motion_id = motion_id; - a1->skeleton_select = mot_load_file(&a1->mot, v4->data_file); - } - else { - a1->skeleton_select = 0; - a1->motion_id = -1; - } - } - a1->frame = -1.0f; - a1->field_68.field_0 = 0; - a1->field_68.field_4 = 0.0f; - return v4; -} - -static void sub_140414ED0(mot_blend* a1, int32_t motion_id) { - mot_key_data_load_file(&a1->mot_key_data, motion_id); - a1->blend.base.__vftable->Reset(&a1->blend.base); -} - -static void sub_14041BE50(rob_chara_bone_data* rob_bone_data, int32_t motion_id) { - sub_14041AD90(rob_bone_data); - mot_blend* v3 = &rob_bone_data->face; - sub_140412F20(v3, &rob_bone_data->motion_loaded.head->next->value->bone_data.bones); - sub_1404146F0(&v3->field_0); - sub_140414ED0(v3, motion_id); -} - -static void sub_14041ABA0(rob_chara_bone_data* a1) { - list_ptr_motion_blend_mot_node* v1 = a1->motion_loaded.head; - list_ptr_motion_blend_mot_node* v3 = v1->next; - while (v3 != v1) { - if (a1->face.mot_key_data.key_sets_ready - && a1->face.mot_key_data.mot_data && !a1->face.field_30) { - if (!a1->disable_eye_motion || a1->eye.mot_key_data.key_sets_ready - && a1->eye.mot_key_data.mot_data && !a1->eye.field_30) - sub_140413EA0(&v3->value->field_0, sub_14041B4E0); - else - sub_140413EA0(&v3->value->field_0, sub_14041B580); - } - else { - if (a1->mouth.mot_key_data.key_sets_ready - && a1->mouth.mot_key_data.mot_data && !a1->mouth.field_30) - sub_140413EA0(&v3->value->field_0, sub_14041B800); - if (a1->eyelid.mot_key_data.key_sets_ready - && a1->eyelid.mot_key_data.mot_data && !a1->eyelid.field_30) { - if (!a1->disable_eye_motion - || a1->eye.mot_key_data.key_sets_ready - && a1->eye.mot_key_data.mot_data && !a1->eye.field_30) - sub_140413EA0(&v3->value->field_0, sub_14041B1C0); - else - sub_140413EA0(&v3->value->field_0, sub_14041B300); - } - else if (a1->eye.mot_key_data.key_sets_ready - && a1->eye.mot_key_data.mot_data && !a1->eye.field_30) - sub_140413EA0(&v3->value->field_0, sub_14041B440); - } - - if (a1->hand_l.mot_key_data.key_sets_ready - && a1->hand_l.mot_key_data.mot_data && !a1->hand_l.field_30) - sub_140413EA0(&v3->value->field_0, sub_14041B6B0); - if (a1->hand_r.mot_key_data.key_sets_ready - && a1->hand_r.mot_key_data.mot_data && !a1->hand_r.field_30) - sub_140413EA0(&v3->value->field_0, sub_14041B750); - v3 = v3->next; - } - sub_14041AD90(a1); - sub_14041AD50(a1); -} - -static bool sub_140413630(struc_308* a1) { - return (a1->field_4 & 2) != 0; -} - -static bool sub_140413790(struc_308* a1) { - return (a1->field_0 & 2) != 0; -} - -static bool sub_1404137A0(struc_308* a1) { - return (a1->field_0 & 0x10) != 0; -} - -static void sub_140415A10(motion_blend_mot* a1) { - MotionBlend* v1 = a1->blend; - if (!v1) - return; - - struc_400 v7; - v7.field_0 = 0; - v7.field_2 = 0; - v7.field_3 = 0; - v7.field_4 = 0; - if (!a1->field_4F8.field_BD) - v7.field_0 = !sub_140413630(&a1->field_4F8); - if (!a1->field_4F8.field_BC) - v7.field_2 = !sub_140413790(&a1->field_4F8); - v7.field_1 = sub_1404137A0(&a1->field_4F8); - v7.frame = a1->mot_key_data.frame; - v7.rot_y = a1->field_4F8.rot_y; - v7.prev_rot_y = a1->field_4F8.prev_rot_y; - v1->__vftable->Step(v1, &v7); -} - -static bool sub_140410A20(motion_blend_mot* a1) { - MotionBlend* v1 = a1->blend; - return !v1 || !v1->rot_y; -} - -static void sub_140415B30(mot_blend* a1) { - struc_400 v3; - v3.field_0 = false; - v3.field_1 = false; - v3.field_2 = false; - v3.field_3 = false; - v3.field_4 = false; - v3.frame = 0.0f; - v3.rot_y = 0.0f; - v3.prev_rot_y = 0.0f; - a1->blend.base.__vftable->Step(&a1->blend.base, &v3); -} - -static void sub_14041DAC0(rob_chara_bone_data* a1) { - void(*rob_chara_bone_data_motion_blend_mot_list_free)(rob_chara_bone_data * rob_bone_data, size_t a2) - = (void(*)(rob_chara_bone_data*, size_t))0x0000000140418E80; - - size_t v2 = 0; - bool v3 = 0; - list_ptr_motion_blend_mot_node* v1 = a1->motion_loaded.head; - list_ptr_motion_blend_mot_node* v4 = v1->next; - while (v4 != v1) { - if (sub_1404136B0(v4->value)) - sub_140415A10(v4->value); - else { - if (v3) { - rob_chara_bone_data_motion_blend_mot_list_free(a1, v2); - break; - } - sub_140415A10(v4->value); - v3 = sub_140410A20(v4->value); - } - v4 = v4->next; - v2++; - } - - sub_140415B30(&a1->face); - sub_140415B30(&a1->hand_l); - sub_140415B30(&a1->hand_r); - sub_140415B30(&a1->mouth); - sub_140415B30(&a1->eye); - sub_140415B30(&a1->eyelid); -} - -void rob_chara_bone_data_interpolate(rob_chara_bone_data* rob_bone_data) { - if (!rob_bone_data->motion_loaded.size) - return; - - sub_14041ABA0(rob_bone_data); - sub_14041DAC0(rob_bone_data); - - list_ptr_motion_blend_mot_node* v2 = rob_bone_data->motion_loaded.head; - list_ptr_motion_blend_mot_node* v3 = v2->next; - while (v3 != v2) { - motion_blend_mot_interpolate(v3->value); - v3 = v3->next; - } - - bone_database_skeleton_type skeleton_type = rob_bone_data->base_skeleton_type; - motion_blend_mot* v5 = rob_bone_data->motion_loaded.head->next->value; - vector_old_bone_data* bones = &v5->bone_data.bones; - vector_old_uint16_t* bone_indices = &v5->bone_data.bone_indices; - mot_blend_interpolate(&rob_bone_data->face, bones, bone_indices, skeleton_type); - mot_blend_interpolate(&rob_bone_data->hand_l, bones, bone_indices, skeleton_type); - mot_blend_interpolate(&rob_bone_data->hand_r, bones, bone_indices, skeleton_type); - mot_blend_interpolate(&rob_bone_data->mouth, bones, bone_indices, skeleton_type); - mot_blend_interpolate(&rob_bone_data->eye, bones, bone_indices, skeleton_type); - mot_blend_interpolate(&rob_bone_data->eyelid, bones, bone_indices, skeleton_type); -} - -static void sub_140412E10(motion_blend_mot* a1, int32_t a2) { - for (bone_data* i = a1->bone_data.bones.begin; i != a1->bone_data.bones.end; i++) { - motion_bone_index v5 = i->motion_bone_index; - if (!(a1->field_0.field_8.bitfield.begin[v5 >> 5] & (1 << (v5 & 0x1F)))) - continue; - - sub_1401EB1D0(i, a2); - if (i->type == BONE_DATABASE_BONE_POSITION_ROTATION && a1->field_4F8.field_0 & 2) - vec3_add(i->trans_prev[a2], a1->field_4F8.field_90, i->trans_prev[a2]); - } -} - -static void sub_1401EAD00(bone_data* a1, bone_data* a2) { - if (!a1->flags) - return; - - if (a1->type >= BONE_DATABASE_BONE_TYPE_1 && a1->type <= BONE_DATABASE_BONE_POSITION_ROTATION) { - a1->trans = a2->trans; - a1->trans_prev[0] = a2->trans_prev[0]; - } - - switch (a1->type) { - case BONE_DATABASE_BONE_ARM_IK_ROTATION: - case BONE_DATABASE_BONE_LEGS_IK_ROTATION: - a1->rot_mat[2] = a2->rot_mat[2]; - a1->rot_mat_prev[2][0] = a2->rot_mat_prev[2][0]; - case BONE_DATABASE_BONE_HEAD_IK_ROTATION: - a1->rot_mat[1] = a2->rot_mat[1]; - a1->rot_mat_prev[1][0] = a2->rot_mat_prev[1][0]; - case BONE_DATABASE_BONE_ROTATION: - case BONE_DATABASE_BONE_TYPE_1: - case BONE_DATABASE_BONE_POSITION: - case BONE_DATABASE_BONE_POSITION_ROTATION: - default: - a1->rot_mat[0] = a2->rot_mat[0]; - a1->rot_mat_prev[0][0] = a2->rot_mat_prev[0][0]; - break; - } -} - -static void sub_140412BB0(motion_blend_mot* a1, vector_old_bone_data* a2) { - for (bone_data* i = a1->bone_data.bones.begin; i != a1->bone_data.bones.end; i++) - sub_1401EAD00(i, &a2->begin[i->motion_bone_index]); -} - -static void sub_1403FBF10(MotionBlend* a1, float_t a2) { - a1->blend = clamp_def(a2, 0.0f, 1.0f); - a1->enable = true; - a1->rot_y = true; -} - -static void sub_1404148C0(motion_blend_mot* a1, float_t a2) { - MotionBlend* v2 = a1->blend; - if (v2) - sub_1403FBF10(v2, a2); -} - -static void sub_1404147F0(motion_blend_mot* a1, MotionBlendType type, float_t blend) { - if (type == MOTION_BLEND_FREEZE) { - a1->freeze.base.__vftable->Reset(&a1->freeze.base); - a1->blend = &a1->freeze.base; - sub_140412E10(a1, 0); - } - else if (type == MOTION_BLEND_CROSS) { - a1->cross.base.__vftable->Reset(&a1->cross.base); - a1->blend = &a1->cross.base; - } - else if (type == MOTION_BLEND_COMBINE) { - a1->combine.base.base.__vftable->Reset(&a1->combine.base.base); - a1->blend = &a1->combine.base.base; - sub_1404148C0(a1, blend); - } - else - a1->blend = 0; -} - -static int32_t sub_1401F0E70(bone_database_skeleton_type type, const char* name) { - bone_database_struct* bone_database - = *(bone_database_struct**)0x0000000140FBC1C0; - int32_t(*sub_1401F0EA0)(const char* a1, const char* a2) - = (int32_t(*)(const char*, const char*))0x00000001401F0EA0; - - if (!bone_database || type == BONE_DATABASE_SKELETON_NONE) - return -1; - else - return sub_1401F0EA0(bone_database->skeleton_names[type], name); -} - -static void bone_data_parent_load_bone_indices_from_mot(bone_data_parent* a1, mot_data* a2) { - if (!a2) - return; - - const char** (*sub_140403B00)() - = (const char** (*)())0x0000000140403B00; - void (*sub_14040F210)(vector_old_uint16_t * a1, size_t a2) - = (void (*)(vector_old_uint16_t*, size_t))0x000000014040F210; - - const char** v4 = sub_140403B00(); - vector_old_uint16_t* v5 = &a1->bone_indices; - uint16_t v6 = a2->data_file->info; - a1->bone_indices.end = a1->bone_indices.begin; - bone_database_skeleton_type v8 = a1->rob_bone_data->base_skeleton_type; - uint16_t v9 = (v6 & 0x3FFF) - 1; - if (!v9) - return; - - for (size_t v10 = 0, v11 = 0; v10 < v9; v11++) { - int32_t v12 = sub_1401F0E70(v8, v4[a2->bone_info[v11]]); - if (v12 == -1) { - v12 = sub_1401F0E70(v8, v4[a2->bone_info[++v11]]); - if (v12 == -1) - break; - } - - uint16_t* v14 = v5->end; - bone_data* v15 = &a1->bones.begin[v12]; - uint16_t v21 = (uint16_t)v12; - if (&v21 >= v14 || v5->begin > &v21) { - if (v14 == v5->capacity_end) - sub_14040F210(v5, 1); - - uint16_t* v18 = v5->end; - if (v18) - *v18 = v21; - } - else { - size_t v16 = &v21 - v5->begin; - if (v14 == v5->capacity_end) - sub_14040F210(v5, 1); - - uint16_t* v17 = v5->end; - if (v17) - *v17 = v5->begin[v16]; - } - v5->end++; - - v15->key_set_offset = (int32_t)v10; - v15->frame = -1.0f; - if (v15->type >= BONE_DATABASE_BONE_POSITION_ROTATION) - v10 += 6; - else - v10 += 3; - } -} - -static void sub_140413C30(struc_308* a1) { - a1->field_8C = false; - a1->field_4C = a1->mat; - a1->mat = mat4_identity; -} - -static void motion_blend_mot_load_file(motion_blend_mot* a1, - int32_t motion_id, MotionBlendType a3, float_t blend) { - sub_1404147F0(a1, a3, blend); - mot_data* v5 = mot_key_data_load_file(&a1->mot_key_data, motion_id); - bone_data_parent* v6 = &a1->bone_data; - if (v5) { - bone_data_parent_load_bone_indices_from_mot(v6, v5); - uint16_t* v7 = a1->bone_data.bone_indices.begin; - uint16_t* v8 = a1->bone_data.bone_indices.end; - bone_data* v9 = a1->bone_data.bones.begin; - for (; v7 != v8; v7++) - v9[*v7].frame = -1.0f; - } - else { - rob_chara_bone_data* rob_bone_data = v6->rob_bone_data; - for (mat4* i = rob_bone_data->mats.begin; i != rob_bone_data->mats.end; i++) - *i = mat4_identity; - - for (mat4* i = rob_bone_data->mats2.begin; i != rob_bone_data->mats2.end; i++) - *i = mat4_identity; - - a1->bone_data.bone_indices.end = a1->bone_data.bone_indices.begin; - } - sub_140413C30(&a1->field_4F8); -} - -static void sub_14041BA60(rob_chara_bone_data* rob_bone_data) { - motion_blend_mot* v1 = rob_bone_data->motion_loaded.head->next->value; - if (v1) - sub_1404146F0(&v1->field_0); -} - -static void sub_14041AE40(rob_chara_bone_data* a1) { - list_ptr_motion_blend_mot_node* v1 = a1->motion_loaded.head; - struc_240* v3 = &v1->next->value->field_0; - if (!v3) - return; - - if (a1->face.mot_key_data.key_sets_ready - && a1->face.mot_key_data.mot_data && !a1->face.field_30) { - if (!a1->disable_eye_motion || a1->eye.mot_key_data.key_sets_ready - && a1->eye.mot_key_data.mot_data && !a1->eye.field_30) - sub_140413EA0(v3, sub_14041B4E0); - else - sub_140413EA0(v3, sub_14041B580); - } - else { - if (a1->mouth.mot_key_data.key_sets_ready - && a1->mouth.mot_key_data.mot_data && !a1->mouth.field_30) - sub_140413EA0(&v1->next->value->field_0, sub_14041B800); - else if (a1->eyelid.mot_key_data.key_sets_ready - && a1->eyelid.mot_key_data.mot_data && !a1->eyelid.field_30) { - if (!a1->disable_eye_motion || a1->eye.mot_key_data.key_sets_ready - && a1->eye.mot_key_data.mot_data && !a1->eye.field_30) - sub_140413EA0(v3, sub_14041B1C0); - else - sub_140413EA0(v3, sub_14041B300); - } - else if (a1->eye.mot_key_data.key_sets_ready - && a1->eye.mot_key_data.mot_data && !a1->eye.field_30) - sub_140413EA0(v3, sub_14041B440); - } - - if (a1->hand_l.mot_key_data.key_sets_ready - && a1->hand_l.mot_key_data.mot_data && !a1->hand_l.field_30) - sub_140413EA0(v3, sub_14041B6B0); - if (a1->hand_r.mot_key_data.key_sets_ready - && a1->hand_r.mot_key_data.mot_data && !a1->hand_r.field_30) - sub_140413EA0(v3, sub_14041B750); -} - -void rob_chara_bone_data_motion_load(rob_chara_bone_data* rob_bone_data, - int32_t motion_id, MotionBlendType motion_blend_type) { - void(*rob_chara_bone_data_motion_blend_mot_list_init)(rob_chara_bone_data * rob_bone_data) - = (void(*)(rob_chara_bone_data*))0x000000014041A9E0; - void(*rob_chara_bone_data_motion_blend_mot_list_free)(rob_chara_bone_data * rob_bone_data, size_t a2) - = (void(*)(rob_chara_bone_data*, size_t))0x0000000140418E80; - list_ptr_motion_blend_mot_node* (*sub_140415D30)(list_ptr_motion_blend_mot * a1, - list_ptr_motion_blend_mot_node * a2, list_ptr_motion_blend_mot_node * a3, motion_blend_mot * *a4) - = (list_ptr_motion_blend_mot_node * (*)(list_ptr_motion_blend_mot*, - list_ptr_motion_blend_mot_node*, list_ptr_motion_blend_mot_node*, - motion_blend_mot**))0x0000000140415D30; - list_size_t_node* (*sub_140415DB0)(list_size_t_node * *a1, list_size_t_node * a2, - list_size_t_node * a3, size_t * a4) = (list_size_t_node * (*)(list_size_t_node**, - list_size_t_node*, list_size_t_node*, size_t*))0x0000000140415DB0; - motion_blend_mot* (*sub_140411A70)(motion_blend_mot * a1, rob_chara_bone_data * rob_bone_data) - = (motion_blend_mot * (*)(motion_blend_mot*, rob_chara_bone_data*))0x0000000140411A70; - - if (!rob_bone_data->motion_loaded.size) - return; - if (motion_blend_type == MOTION_BLEND_FREEZE) { - rob_chara_bone_data_motion_blend_mot_list_free(rob_bone_data, 1); - sub_14041AE40(rob_bone_data); - list_ptr_motion_blend_mot_node*v15 = rob_bone_data->motion_loaded.head; - motion_blend_mot_load_file(v15->next->value, motion_id, MOTION_BLEND_FREEZE, 1.0f); - sub_14041BA60(rob_bone_data); - return; - } - - if (motion_blend_type != MOTION_BLEND_CROSS) { - if (motion_blend_type == MOTION_BLEND_COMBINE) - motion_blend_type = MOTION_BLEND; - rob_chara_bone_data_motion_blend_mot_list_free(rob_bone_data, 1); - motion_blend_mot_load_file(rob_bone_data->motion_loaded.head->next->value, motion_id, motion_blend_type, 1.0f); - return; - } - - rob_chara_bone_data_motion_blend_mot_list_free(rob_bone_data, 2); - if (rob_bone_data->motion_indices.size) { - motion_blend_mot* v6 = rob_bone_data->motion_loaded.head->next->value; - rob_chara_bone_data_motion_blend_mot_list_init(rob_bone_data); - sub_140412BB0(rob_bone_data->motion_loaded.head->next->value, &v6->bone_data.bones); - motion_blend_mot_load_file(rob_bone_data->motion_loaded.head->next->value, motion_id, MOTION_BLEND_CROSS, 1.0f); - return; - } - - list_ptr_motion_blend_mot* v7 = &rob_bone_data->motion_loaded; - motion_blend_mot* v16 = sub_140411A70(rob_bone_data->motion_loaded.head->next->value, rob_bone_data); - if (!v16) { - rob_chara_bone_data_motion_blend_mot_list_free(rob_bone_data, 1); - sub_14041AE40(rob_bone_data); - list_ptr_motion_blend_mot_node* v15 = v7->head; - motion_blend_mot_load_file(v15->next->value, motion_id, MOTION_BLEND_FREEZE, 1.0f); - sub_14041BA60(rob_bone_data); - return; - } - - list_ptr_motion_blend_mot_node* v8 = v7->head->next; - motion_blend_mot* v9 = v8->value; - list_ptr_motion_blend_mot_node* v10 = sub_140415D30(&rob_bone_data->motion_loaded, v8, v8->prev, &v16); - rob_bone_data->motion_loaded.size++; - v8->prev = v10; - v10->prev->next = v10; - size_t v17 = vector_old_length(rob_bone_data->motions); - list_size_t_node* v12 = rob_bone_data->motion_loaded_indices.head->next; - list_size_t_node* v13 = sub_140415DB0(&rob_bone_data->motion_loaded_indices.head, v12, v12->prev, &v17); - rob_bone_data->motion_loaded_indices.size++; - v12->prev = v13; - v13->prev->next = v13; - sub_140412BB0(v7->head->next->value, &v9->bone_data.bones); - motion_blend_mot_load_file(v7->head->next->value, motion_id, MOTION_BLEND_CROSS, 1.0f); -} - -static void sub_14041BFC0(rob_chara_bone_data* rob_bone_data, int32_t motion_id) { - sub_140412F20(&rob_bone_data->hand_l, &rob_bone_data->motion_loaded.head->next->value->bone_data.bones); - sub_140414ED0(&rob_bone_data->hand_l, motion_id); -} - -static void sub_14041C260(rob_chara_bone_data* rob_bone_data, int32_t motion_id) { - sub_140412F20(&rob_bone_data->hand_r, &rob_bone_data->motion_loaded.head->next->value->bone_data.bones); - sub_140414ED0(&rob_bone_data->hand_r, motion_id); -} - -static void sub_14041D200(rob_chara_bone_data* rob_bone_data, int32_t motion_id) { - sub_140412F20(&rob_bone_data->mouth, &rob_bone_data->motion_loaded.head->next->value->bone_data.bones); - sub_140414ED0(&rob_bone_data->mouth, motion_id); -} - -static void sub_14041BDB0(rob_chara_bone_data* rob_bone_data, int32_t motion_id) { - sub_140412F20(&rob_bone_data->eye, &rob_bone_data->motion_loaded.head->next->value->bone_data.bones); - sub_140414ED0(&rob_bone_data->eye, motion_id); -} - -static void sub_14041BD20(rob_chara_bone_data* rob_bone_data, int32_t motion_id) { - sub_14041AD50(rob_bone_data); - mot_blend* v3 = &rob_bone_data->eyelid; - sub_140412F20(v3, &rob_bone_data->motion_loaded.head->next->value->bone_data.bones); - sub_1404146F0(&v3->field_0); - sub_140414ED0(v3, motion_id); -} - -static void sub_140414BC0(mot_play_frame_data* a1, float_t frame) { - float_t v2 = a1->field_14; - a1->frame = frame; - a1->field_28 = 0; - if (v2 >= 0.0f && (a1->field_18 == 2 && frame <= v2) || frame >= v2) - a1->field_14 = -1.0; -} - -static void rob_chara_bone_data_set_frame(rob_chara_bone_data* a1, float_t frame) { - sub_140414BC0(&a1->motion_loaded.head->next->value->mot_play_data.frame_data, frame); -} - -static void sub_14041C680(rob_chara_bone_data* a1, char a2) { - struc_308* v2 = &a1->motion_loaded.head->next->value->field_4F8; - if (a2) - v2->field_0 |= 2; - else - v2->field_0 &= ~2; -} - -static void sub_14041C9D0(rob_chara_bone_data* a1, char a2) { - struc_308* v2 = &a1->motion_loaded.head->next->value->field_4F8; - v2->field_8 = (uint8_t)(v2->field_0 & 1); - if (a2) - v2->field_0 |= 1; - else - v2->field_0 &= ~1; -} - -static void sub_14041D2D0(rob_chara_bone_data* a1, char a2) { - struc_308* v2 = &a1->motion_loaded.head->next->value->field_4F8; - if (a2) - v2->field_0 |= 4; - else - v2->field_0 &= ~4; -} - -static void sub_14041BC40(rob_chara_bone_data* a1, char a2) { - struc_308* v2 = &a1->motion_loaded.head->next->value->field_4F8; - if (a2) - v2->field_0 |= 8; - else - v2->field_0 &= ~8; -} - -static void sub_140414F00(struc_308* a1, float_t a2) { - a1->prev_rot_y = a1->rot_y; - a1->rot_y = a2; -} - -static void sub_14041D270(rob_chara_bone_data* a1, float_t a2) { - sub_140414F00(&a1->motion_loaded.head->next->value->field_4F8, a2); -} - -static void sub_14041D2A0(rob_chara_bone_data* a1, float_t a2) { - a1->motion_loaded.head->next->value->field_4F8.prev_rot_y = a2; -} - -static void rob_chara_bone_data_set_rot_y(rob_chara_bone_data* rob_bone_data, float_t rot_y) { - rob_bone_data->motion_loaded.head->next->value->bone_data.rot_y = rot_y; -} - -static void sub_1403FBED0(MotionBlend* a1, float_t duration, float_t step, float_t offset) { - if (duration < 0.0f) { - a1->enable = false; - a1->duration = 0.0f; - a1->frame = 0.0f; - a1->step = step; - a1->offset = offset; - } - else { - a1->enable = true; - a1->duration = duration; - a1->frame = 0.0f; - a1->step = step; - a1->offset = offset; - } -} - -static void motion_blend_mot_set_duration(motion_blend_mot* a1, - float_t duration, float_t step, float_t offset) { - MotionBlend* v4 = a1->blend; - if (v4) - sub_1403FBED0(v4, duration, step, offset); -} - -static void rob_chara_bone_data_set_motion_duration(rob_chara_bone_data* a1, - float_t duration, float_t step, float_t offset) { - motion_blend_mot_set_duration(a1->motion_loaded.head->next->value, duration, step, offset); -} - -static void sub_14041D310(rob_chara_bone_data* a1, float_t a2, float_t a3, int32_t a4) { - MotionBlend* v1 = a1->motion_loaded.head->next->value->blend; - if (v1) - v1->__vftable->Field10(v1, a2, a3, a4); -} - -static void rob_chara_load_default_motion_sub(rob_chara* a1, int32_t a2, int32_t motion_id) { - sub_14041BE50(a1->bone_data, -1); - sub_14041BFC0(a1->bone_data, -1); - sub_14041C260(a1->bone_data, -1); - sub_14041D200(a1->bone_data, -1); - sub_14041BDB0(a1->bone_data, -1); - sub_14041BD20(a1->bone_data, -1); - rob_chara_bone_data_motion_load(a1->bone_data, motion_id, MOTION_BLEND_FREEZE); - rob_chara_bone_data_set_frame(a1->bone_data, 0.0f); - sub_14041C680(a1->bone_data, 0); - sub_14041C9D0(a1->bone_data, 0); - sub_14041D2D0(a1->bone_data, 0); - sub_14041BC40(a1->bone_data, 0); - sub_14041D270(a1->bone_data, 0.0f); - sub_14041D2A0(a1->bone_data, 0.0f); - rob_chara_bone_data_set_rot_y(a1->bone_data, 0.0f); - rob_chara_bone_data_set_motion_duration(a1->bone_data, 0.0f, 1.0f, 1.0f); - sub_14041D310(a1->bone_data, 0.0f, 0.0f, 2); - rob_chara_bone_data_interpolate(a1->bone_data); - rob_chara_bone_data_update(a1->bone_data, 0); - sub_140412E10(a1->bone_data->motion_loaded.head->next->value, a2); -} - -int32_t rob_cmn_mottbl_get_motion_index(rob_chara* a1, int32_t a2) { - struc_403* rob_cmn_mottbl_data = *rob_cmn_mottbl_data_ptr; - - if (a2 == 224) - return a1->data.motion.motion_id; - if (a2 >= 214 && a2 <= 223) - return ((uint32_t*)&a1->data_prev.field_1E68.field_1C88)[a2]; - if (a2 >= 226 && a2 <= 235) - return ((uint32_t*)&a1->data_prev.field_1E68.field_1C78)[a2 + 1]; - - size_t v7 = a1->data.field_8.field_1A4; - size_t v8 = a1->chara_index; - if (!rob_cmn_mottbl_data - || v8 < 0 || v8 >= rob_cmn_mottbl_data->field_0 - || a2 < 0 || a2 >= rob_cmn_mottbl_data->field_4) - return -1; - - struc_404* v4 = (struc_404*)((size_t)rob_cmn_mottbl_data + rob_cmn_mottbl_data->field_8); - if (v7 < v4[v8].field_4) - return ((int32_t*)((size_t)rob_cmn_mottbl_data - + ((uint32_t*)((size_t)rob_cmn_mottbl_data + v4[v8].field_0))[v7]))[a2]; - return -1; -} - -static void rob_chara_load_default_motion(rob_chara* a1) { - rob_chara_load_default_motion_sub(a1, 1, rob_cmn_mottbl_get_motion_index(a1, 0)); -} - -static bone_node* rob_chara_bone_data_get_node(rob_chara_bone_data* rob_bone_data, size_t index) { - if ((ssize_t)index < vector_old_length(rob_bone_data->nodes)) - return &rob_bone_data->nodes.begin[index]; - return 0; -} - -static char* ExNodeBlock_get_parent_name(ExNodeBlock* a1) { - return string_data(&a1->parent_name); -} - -static bone_node* rob_chara_item_equip_object_get_bone_node( - rob_chara_item_equip_object* a1, int32_t a2) { - bool ex_data = a2 & 0x8000 ? true : false; - a2 &= 0x7FFF; - if (!ex_data) - return &a1->bone_nodes[a2]; - else if (a2 < vector_old_length(a1->ex_data_bone_nodes)) - return &a1->ex_data_bone_nodes.begin[a2]; - return 0; -} - -static bone_node* rob_chara_item_equip_object_get_bone_node_by_name( - rob_chara_item_equip_object* a1, char* a2) { - int32_t(*rob_chara_item_equip_object_get_bone_index)(rob_chara_item_equip_object * a1, char* a2) - = (int32_t(*)(rob_chara_item_equip_object * , char* ))0x00000001405F3400; - return rob_chara_item_equip_object_get_bone_node(a1, - rob_chara_item_equip_object_get_bone_index(a1, a2)); -} - -static void rob_chara_item_equip_object_get_parent_bone_nodes( - rob_chara_item_equip_object* a1, bone_node* a2) { - a1->bone_nodes = a2; - a1->mat = a2->mat; - for (ExNodeBlock** i = a1->node_blocks.begin; i != a1->node_blocks.end; i++) - (*i)->parent_bone_node = rob_chara_item_equip_object_get_bone_node_by_name( - a1, ExNodeBlock_get_parent_name(*i)); -} - -static void rob_chara_item_equip_get_parent_bone_nodes(rob_chara_item_equip* a1, bone_node* a2) { - a1->bone_nodes = a2; - a1->matrices = a2->mat; - for (int32_t v2 = a1->first_item_equip_object; v2 < a1->max_item_equip_object; v2++) - rob_chara_item_equip_object_get_parent_bone_nodes(&a1->item_equip_object[v2], a1->bone_nodes); -} - -static bool rob_chara_check_for_ageageagain_module(chara_index chara_index, int32_t module_index) { - return chara_index == CHARA_MIKU && module_index == 148; -} - -static void sub_140517060(rob_chara* a1, bool a2) { - void(*sub_140543780)(int32_t a1, int32_t a2, char a3) - = (void(*)(int32_t, int32_t, char))0x0000000140543780; - - a1->data.field_0 &= ~1; - a1->data.field_3 &= ~1; - a1->data.field_0 |= a2 & 1; - a1->data.field_3 |= a2 & 1; - if (rob_chara_check_for_ageageagain_module(a1->chara_index, a1->module_index)) { - sub_140543780(a1->chara_id, 1, a2); - sub_140543780(a1->chara_id, 2, a2); - } -} - -static float_t chara_size_table_get_value(uint32_t a1) { - static const float_t chara_size_table[] = { - 1.07f, 1.0f, 0.96f, 0.987f, 1.025f - }; - - if (a1 > 4) - return 1.0f; - else - return chara_size_table[a1]; -} - -static float_t chara_pos_adjust_y_table_get_value(uint32_t a1) { - static const float_t chara_pos_adjust_y_table[] = { - 0.071732f, 0.0f, -0.03859f, 0.0f, 0.0f - }; - - if (a1 > 4) - return 0.0f; - else - return chara_pos_adjust_y_table[a1]; -} - -static void sub_14041C5C0(rob_chara_bone_data* a1, struc_243* a2) { - a1->field_788.field_15C = *a2; -} - -static void sub_14041D4E0(rob_chara_bone_data* a1, struc_242* a2) { - a1->field_958.field_0 = *a2; -} - -static void sub_14041D5C0(rob_chara_bone_data* a1, struc_242* a2) { - a1->field_958.field_2C = *a2; -} - -static void sub_14041D560(rob_chara_bone_data* a1, char a2, char a3) { - a1->field_958.field_58 = a2; - a1->field_958.field_59 = a3; -} - -static void rob_chara_bone_data_ik_scale_calculate( - rob_chara_bone_data_ik_scale* a1, vector_old_bone_data* a2, - bone_database_skeleton_type base_skeleton_type, - bone_database_skeleton_type skeleton_type) { - bone_data* b_cl_mune = &a2->begin[MOTION_BONE_CL_MUNE]; - bone_data* b_kl_kubi = &a2->begin[MOTION_BONE_KL_KUBI]; - bone_data* b_c_kata_l = &a2->begin[MOTION_BONE_C_KATA_L]; - bone_data* b_cl_momo_l = &a2->begin[MOTION_BONE_CL_MOMO_L]; - - float_t base_height = fabsf(b_cl_momo_l->base_translation[0].y) + b_cl_momo_l->ik_segment_length[0] - + b_cl_momo_l->ik_2nd_segment_length[0] + bone_database_get_skeleton_heel_height(base_skeleton_type); - float_t height = fabsf(b_cl_momo_l->base_translation[1].y) + b_cl_momo_l->ik_segment_length[1] - + b_cl_momo_l->ik_2nd_segment_length[1] + bone_database_get_skeleton_heel_height(skeleton_type); - a1->ratio0 = height / base_height; - a1->ratio1 = (fabsf(b_kl_kubi->base_translation[1].y) + b_cl_mune->ik_segment_length[1]) - / (fabsf(b_kl_kubi->base_translation[0].y) + b_cl_mune->ik_segment_length[0]); - a1->ratio2 = (b_c_kata_l->ik_segment_length[1] + b_c_kata_l->ik_2nd_segment_length[1]) - / (b_c_kata_l->ik_segment_length[0] + b_c_kata_l->ik_2nd_segment_length[0]); - a1->ratio3 = (fabsf(b_kl_kubi->base_translation[1].y) + b_cl_mune->ik_segment_length[1] + height) - / (fabsf(b_kl_kubi->base_translation[0].y) + b_cl_mune->ik_segment_length[0] + base_height); -} - -static void rob_chara_bone_data_get_ik_scale(rob_chara_bone_data* a1) { - if (!a1->motion_loaded.size) - return; - - motion_blend_mot* v2 = a1->motion_loaded.head->next->value; - rob_chara_bone_data_ik_scale_calculate(&a1->ik_scale, &v2->bone_data.bones, a1->base_skeleton_type, a1->skeleton_type); - float_t ratio0 = a1->ik_scale.ratio0; - v2->field_4F8.field_C0 = ratio0; - v2->field_4F8.field_C4 = ratio0; - v2->field_4F8.field_C8 = 0.0f; -} - -static float_t rob_chara_bone_data_get_ik_scale_ratio0(rob_chara_bone_data* a1) { - return a1->ik_scale.ratio0; -} - -static void sub_1405167A0(rob_chara* a1) { - a1->field_C = 1; -} - -static void sub_140513C60(rob_chara_item_equip* a1, int32_t a2, bool a3) { - if (a3) - a1->field_18[a2] &= ~4; - else - a1->field_18[a2] |= 4; -} - -static object_info sub_140525C00(rob_chara_item_cos_data* a1, int32_t a2) { - tree_pair_int32_t_object_info_node* v2 = a1->head_replace.head; - tree_pair_int32_t_object_info_node* v3 = v2; - tree_pair_int32_t_object_info_node* v4 = v2->parent; - while (!v4->is_null) - if (v4->key >= a2) { - v3 = v4; - v4 = v4->left; - } - else - v4 = v4->right; - - tree_pair_int32_t_object_info_node* v6; - if (v3 == v2 || a2 < v3->key) - v6 = v2; - else - v6 = v3; - - if (v6 == v2) - return object_info_null; - else - return v6->value; -} - -static object_info sub_1405104C0(rob_chara* a1, int32_t a2) { - if (a2 > 8) - return object_info_null; - - object_info v3 = sub_140525C00(&a1->item_cos_data, a2); - if (v3.id == -1 && v3.set_id == -1) - return a1->chara_init_data->head_objects[a2]; - return v3; -} - -static void sub_1405139E0(rob_chara_item_equip* a1, uint32_t a2, bool disp) { - a1->item_equip_object[a2].disp = disp; -} - -static void sub_140517120(rob_chara* a1, uint32_t a2, bool disp) { - if (a2 < 31) - sub_1405139E0(a1->item_equip, a2, disp); - else if (a2 == 31) { - void(*sub_140543780)(int32_t a1, int32_t a2, char a3) - = (void(*)(int32_t, int32_t, char))0x0000000140543780; - void(*sub_1405430F0)(int32_t a1, int32_t a2) - = (void(*)(int32_t, int32_t))0x00000001405430F0; - - for (int32_t i = 1; i < 31; i++) { - sub_1405139E0(a1->item_equip, i, disp); - if (i == 1 && rob_chara_check_for_ageageagain_module( - a1->chara_index, a1->module_index)) { - sub_140543780(a1->chara_id, 1, disp); - sub_140543780(a1->chara_id, 2, disp); - sub_1405430F0(a1->chara_id, 1); - sub_1405430F0(a1->chara_id, 2); - } - } - } -} - -void rob_chara_reset_data(rob_chara* a1, rob_chara_pv_data* a2) { - float_t(*sub_140228BD0)(vec3 * a1, float_t a2) - = (float_t(*)(vec3*, float_t))0x0000000140228BD0; - void(*rob_chara_data_reset)(rob_chara_data * a1) - = (void(*)(rob_chara_data*))0x0000000140505EA0; - struc_243* (*sub_140510550)(int32_t a1) = (struc_243 * (*)(int32_t))0x0000000140510550; - struc_241* (*sub_1405106E0)(int32_t a1) = (struc_241 * (*)(int32_t))0x00000001405106E0; - void(*rob_chara_load_motion)(rob_chara * a1, int32_t motion_id, bool a3, float_t a4, int32_t a5) - = (void(*)(rob_chara*, int32_t, bool, float_t, int32_t))0x00000001405552A0; - - int32_t v2 = a1->data.field_8.field_1CC; - rob_chara_bone_data_reset(a1->bone_data); - rob_chara_bone_data_init_data(a1->bone_data, - BONE_DATABASE_SKELETON_COMMON, a1->chara_init_data->skeleton_type); - rob_chara_data_reset(&a1->data); - rob_chara_data_reset(&a1->data_prev); - rob_chara_item_equip_get_parent_bone_nodes(a1->item_equip, - rob_chara_bone_data_get_node(a1->bone_data, 0)); - a1->field_4 = a2->type; - sub_140517060(a1, a2->field_4); - a1->field_A = a2->field_16; - a1->field_1C = 1.0f; - vec3 v8 = a2->field_8; - if (a2->type != ROB_CHARA_TYPE_2) { - vec3 v19 = a2->field_8; - v19.y += 1.0f; - v8.y = sub_140228BD0(&v19, 0.0); - } - - rob_chara_data* v3 = &a1->data; - v3->miku_rot.rot_y_int16 = a2->rot_y_int16; - v3->miku_rot.field_6 = a2->rot_y_int16; - v3->miku_rot.position = v8; - v3->miku_rot.field_24 = v8; - v3->miku_rot.field_48 = v8; - v3->adjust_data.scale = chara_size_table_get_value(a1->pv_data.chara_size_index); - v3->adjust_data.height_adjust = a1->pv_data.height_adjust; - v3->adjust_data.pos_adjust_y = chara_pos_adjust_y_table_get_value(a1->pv_data.chara_size_index); - v3->field_8.field_1A8 = a1->chara_init_data->field_838[v3->field_8.field_1A4]; - sub_14041C5C0(a1->bone_data, sub_140510550(a1->chara_index)); - rob_chara_bone_data_eyes_xrot_adjust(a1->bone_data, - sub_1405106E0(a1->chara_index), &a2->eyes_adjust); - sub_14041D4E0(a1->bone_data, &a2->field_18); - sub_14041D5C0(a1->bone_data, &a2->field_44); - sub_14041D560(a1->bone_data, 1, 0); - rob_chara_bone_data_get_ik_scale(a1->bone_data); - float_t ratio0 = rob_chara_bone_data_get_ik_scale_ratio0(a1->bone_data); - v3->motion.field_138 = ratio0; - v3->motion.field_13C = ratio0; - v3->motion.field_140 = 0; - v3->motion.field_144 = 0; - v3->motion.field_148 = 0; - rob_chara_load_default_motion(a1); - v3->field_8.field_0 = 0; - v3->field_8.field_4.field_0 = 1; - v3->field_8.field_4.motion_id = rob_cmn_mottbl_get_motion_index(a1, 0); - v3->field_8.field_4.field_10 = a2->field_5; - rob_chara_load_motion(a1, v3->field_8.field_4.motion_id, v3->field_8.field_4.field_10, 0.0, 0); - if (!a2->field_6) - v3->field_8.field_1CC = v2; - sub_1405167A0(a1); - a1->item_equip->field_DC = 0; - sub_140513C60(a1->item_equip, 1, false); - sub_140513C60(a1->item_equip, 10, false); - sub_140513C60(a1->item_equip, 11, false); - object_info v17 = sub_1405104C0(a1, 0); - v3->motion.field_150.head_object = v17; - v3->motion.field_3B0.head_object = v17; - sub_140517120(a1, 0x1F, true); - a1->field_20 = 0; -} - static void skin_param_hinge_limit(skin_param_hinge* hinge) { hinge->ymin = max_def(hinge->ymin, -179.0f) * DEG_TO_RAD_FLOAT; hinge->ymax = min_def(hinge->ymax, 179.0f) * DEG_TO_RAD_FLOAT; diff --git a/src/DivaGL/bone_data.hpp b/src/DivaGL/bone_data.hpp index 65884467..3b480fb0 100644 --- a/src/DivaGL/bone_data.hpp +++ b/src/DivaGL/bone_data.hpp @@ -4026,9 +4026,9 @@ vector_old(ex_data_name_bone_index) struct texture_data_struct { int32_t field_0; - vec3 texture_color_coeff; + vec3 texture_color_coefficients; vec3 texture_color_offset; - vec3 texture_specular_coeff; + vec3 texture_specular_coefficients; vec3 texture_specular_offset; }; @@ -4084,7 +4084,7 @@ struct rob_chara_item_equip { item_id field_D4; bool disable_update; int32_t field_DC; - vec4 texture_color_coeff; + vec4 texture_color_coefficients; float_t wet; float_t wind_strength; bool chara_color; @@ -4123,15 +4123,6 @@ struct rob_chara_item_equip { bool parts_white_one_l; }; -extern void bone_data_mult_ik(bone_data* a1, int32_t a2); -extern void bone_data_mult_1(bone_data* a1, mat4* parent_mat, bone_data* a3, bool solve_ik); -extern void bone_data_mult_2(rob_chara_bone_data* a1, mat4* a2); -extern void rob_chara_bone_data_motion_load(rob_chara_bone_data* rob_bone_data, - int32_t motion_id, MotionBlendType blend_type); -extern void rob_chara_bone_data_interpolate(rob_chara_bone_data* a1); -extern void rob_chara_bone_data_update(rob_chara_bone_data* a1, mat4* a2); -extern void rob_chara_reset_data(rob_chara* a1, rob_chara_pv_data* a2); - extern void ExNodeBlock__Field_10(ExNodeBlock* node); extern ExOsageBlock* ExOsageBlock__Dispose(ExOsageBlock* osg, bool dispose); diff --git a/src/DivaGL/inject.cpp b/src/DivaGL/inject.cpp index 6d172b1e..cd8cb407 100644 --- a/src/DivaGL/inject.cpp +++ b/src/DivaGL/inject.cpp @@ -56,25 +56,18 @@ static patch_struct patch_data[] = { { (void*)0x00000001405E4B50, (uint64_t)&shader_bind, }, { (void*)0x00000001401D3860, (uint64_t)&printf_proxy, }, { (void*)0x00000001400DE640, (uint64_t)&printf_proxy, }, - //{ (void*)0x0000000140507210, (uint64_t)&rob_chara_reset_data, }, - //{ (void*)0x00000001401EBD30, (uint64_t)&bone_data_mult_ik, }, - //{ (void*)0x00000001401EB6D0, (uint64_t)&bone_data_mult_1, }, - //{ (void*)0x000000014041DA50, (uint64_t)&bone_data_mult_2, }, - //{ (void*)0x000000014041CF50, (uint64_t)&rob_chara_bone_data_motion_load, }, - //{ (void*)0x0000000140418870, (uint64_t)&rob_chara_bone_data_interpolate, }, - //{ (void*)0x0000000140418A60, (uint64_t)&rob_chara_bone_data_update, }, - //{ (void*)0x00000001405F39E0, (uint64_t)&ExNodeBlock__Field_10, }, - //{ (void*)0x00000001405EEAE0, (uint64_t)&ExOsageBlock__Dispose, }, - //{ (void*)0x00000001405F3640, (uint64_t)&ExOsageBlock__Init, }, - //{ (void*)0x00000001405F49F0, (uint64_t)&ExOsageBlock__Field_18, }, - //{ (void*)0x00000001405F2140, (uint64_t)&ExOsageBlock__Field_20, }, - //{ (void*)0x00000001405F2470, (uint64_t)&ExOsageBlock__SetOsagePlayData, }, - //{ (void*)0x00000001405F26F0, (uint64_t)&ExOsageBlock__Disp, }, - //{ (void*)0x00000001405F2640, (uint64_t)&ExOsageBlock__Reset, }, - //{ (void*)0x00000001405F2860, (uint64_t)&ExOsageBlock__Field_40, }, - //{ (void*)0x00000001405F4640, (uint64_t)&ExOsageBlock__Field_48, }, - //{ (void*)0x00000001405F4730, (uint64_t)&ExOsageBlock__Field_50, }, - //{ (void*)0x00000001405F48F0, (uint64_t)&ExOsageBlock__Field_58, }, + { (void*)0x00000001405F39E0, (uint64_t)&ExNodeBlock__Field_10, }, + { (void*)0x00000001405EEAE0, (uint64_t)&ExOsageBlock__Dispose, }, + { (void*)0x00000001405F3640, (uint64_t)&ExOsageBlock__Init, }, + { (void*)0x00000001405F49F0, (uint64_t)&ExOsageBlock__Field_18, }, + { (void*)0x00000001405F2140, (uint64_t)&ExOsageBlock__Field_20, }, + { (void*)0x00000001405F2470, (uint64_t)&ExOsageBlock__SetOsagePlayData, }, + { (void*)0x00000001405F26F0, (uint64_t)&ExOsageBlock__Disp, }, + { (void*)0x00000001405F2640, (uint64_t)&ExOsageBlock__Reset, }, + { (void*)0x00000001405F2860, (uint64_t)&ExOsageBlock__Field_40, }, + { (void*)0x00000001405F4640, (uint64_t)&ExOsageBlock__Field_48, }, + { (void*)0x00000001405F4730, (uint64_t)&ExOsageBlock__Field_50, }, + { (void*)0x00000001405F48F0, (uint64_t)&ExOsageBlock__Field_58, }, }; void inject_patches() { diff --git a/src/DivaGL/shader_table.cpp b/src/DivaGL/shader_table.cpp index fc8dc64e..0298eba5 100644 --- a/src/DivaGL/shader_table.cpp +++ b/src/DivaGL/shader_table.cpp @@ -59,7 +59,7 @@ enum uniform_name { U_BONE_MAT = 0x30, U_SNOW_PARTICLE = 0x31, U_SPECULAR_IBL = 0x32, - U_SPRITE_BLEND = 0x33, + U_COMBINER = 0x33, U_TEX_0_TYPE = 0x34, U_TEX_1_TYPE = 0x35, U_SSS_FILTER = 0x36, diff --git a/src/KKdLib/a3da.cpp b/src/KKdLib/a3da.cpp index a0ff5c52..6f01c0b3 100644 --- a/src/KKdLib/a3da.cpp +++ b/src/KKdLib/a3da.cpp @@ -90,102 +90,6 @@ static void a3dc_write_a3da_vec3(stream& s, a3da_vec3& value); static void a3dc_read_a3da_vec3_f16(void* data, size_t size, a3da_vec3* value, a3da_compress_f16 f16); static void a3dc_write_a3da_vec3_f16(stream& s, a3da_vec3& value, a3da_compress_f16 f16); -a3da::a3da() : ready(), compressed(), format(), _compress_f16(), -_file_name(), _property_version(), _converter_version() { - -} - -a3da::~a3da() { - -} - -void a3da::read(const char* path) { - if (!path) - return; - - char* path_a3da = str_utils_add(path, (char*)".a3da"); - if (path_check_file_exists(path_a3da)) { - file_stream s; - s.open(path_a3da, "rb"); - if (s.check_not_null()) - a3da_read_inner(this, s); - } - free_def(path_a3da); -} - -void a3da::read(const wchar_t* path) { - if (!path) - return; - - wchar_t* path_a3da = str_utils_add(path, (wchar_t*)L".a3da"); - if (path_check_file_exists(path_a3da)) { - file_stream s; - s.open(path_a3da, L"rb"); - if (s.check_not_null()) - a3da_read_inner(this, s); - } - free_def(path_a3da); -} - -void a3da::read(const void* data, size_t size) { - if (!data || !size) - return; - - memory_stream s; - s.open(data, size); - a3da_read_inner(this, s); -} - -void a3da::write(const char* path) { - if (!path || !ready) - return; - - char* path_a3da = str_utils_add(path, ".a3da"); - file_stream s; - s.open(path_a3da, "wb"); - if (s.check_not_null()) - a3da_write_inner(this, s); - free_def(path_a3da); -} - -void a3da::write(const wchar_t* path) { - if (!path || !ready) - return; - - wchar_t* path_a3da = str_utils_add(path, L".a3da"); - file_stream s; - s.open(path_a3da, L"wb"); - if (s.check_not_null()) - a3da_write_inner(this, s); - free_def(path_a3da); -} - -void a3da::write(void** data, size_t* size) { - if (!data || !size || !ready) - return; - - memory_stream s; - a3da_write_inner(this, s); - s.copy(data, size); -} - -bool a3da::load_file(void* data, const char* path, const char* file, uint32_t hash) { - size_t file_len = utf8_length(file); - - const char* t = strrchr(file, '.'); - if (t) - file_len = t - file; - - std::string s; - s.assign(path); - s.append(file, file_len); - - a3da* a = (a3da*)data; - a->read(s.c_str()); - - return a->ready; -} - a3da_key::a3da_key() : flags(), bin_offset(), type(), ep_type_pre(), ep_type_post(), max_frame(), raw_data(), raw_data_binary(), raw_data_value_list_size(), raw_data_value_list_offset(), value() { } @@ -388,6 +292,102 @@ a3da_post_process::~a3da_post_process() { } +a3da::a3da() : ready(), compressed(), format(), _compress_f16(), +_file_name(), _property_version(), _converter_version() { + +} + +a3da::~a3da() { + +} + +void a3da::read(const char* path) { + if (!path) + return; + + char* path_a3da = str_utils_add(path, (char*)".a3da"); + if (path_check_file_exists(path_a3da)) { + file_stream s; + s.open(path_a3da, "rb"); + if (s.check_not_null()) + a3da_read_inner(this, s); + } + free_def(path_a3da); +} + +void a3da::read(const wchar_t* path) { + if (!path) + return; + + wchar_t* path_a3da = str_utils_add(path, (wchar_t*)L".a3da"); + if (path_check_file_exists(path_a3da)) { + file_stream s; + s.open(path_a3da, L"rb"); + if (s.check_not_null()) + a3da_read_inner(this, s); + } + free_def(path_a3da); +} + +void a3da::read(const void* data, size_t size) { + if (!data || !size) + return; + + memory_stream s; + s.open(data, size); + a3da_read_inner(this, s); +} + +void a3da::write(const char* path) { + if (!path || !ready) + return; + + char* path_a3da = str_utils_add(path, ".a3da"); + file_stream s; + s.open(path_a3da, "wb"); + if (s.check_not_null()) + a3da_write_inner(this, s); + free_def(path_a3da); +} + +void a3da::write(const wchar_t* path) { + if (!path || !ready) + return; + + wchar_t* path_a3da = str_utils_add(path, L".a3da"); + file_stream s; + s.open(path_a3da, L"wb"); + if (s.check_not_null()) + a3da_write_inner(this, s); + free_def(path_a3da); +} + +void a3da::write(void** data, size_t* size) { + if (!data || !size || !ready) + return; + + memory_stream s; + a3da_write_inner(this, s); + s.copy(data, size); +} + +bool a3da::load_file(void* data, const char* path, const char* file, uint32_t hash) { + size_t file_len = utf8_length(file); + + const char* t = strrchr(file, '.'); + if (t) + file_len = t - file; + + std::string s; + s.assign(path); + s.append(file, file_len); + + a3da* a = (a3da*)data; + a->read(s.c_str()); + + return a->ready; +} + static void a3da_read_inner(a3da* a, stream& s) { a3dc_header header = {}; @@ -618,9 +618,8 @@ static void a3da_read_text(a3da* a, void* data, size_t size) { int32_t count; if (kv.read("ambient", "length", count)) { - std::vector& va = a->ambient; - - va.resize(count); + a->ambient.resize(count); + a3da_ambient* va = a->ambient.data(); for (int32_t i = 0; i < count; i++) { if (!kv.open_scope_fmt(i)) continue; @@ -638,9 +637,8 @@ static void a3da_read_text(a3da* a, void* data, size_t size) { } if (kv.read("auth_2d", "length", count)) { - std::vector& va2 = a->auth_2d; - - va2.resize(count); + a->auth_2d.resize(count); + std::string* va2 = a->auth_2d.data(); for (int32_t i = 0; i < count; i++) { if (!kv.open_scope_fmt(i)) continue; @@ -652,9 +650,8 @@ static void a3da_read_text(a3da* a, void* data, size_t size) { } if (kv.read("camera_root", "length", count)) { - std::vector& vcr = a->camera_root; - - vcr.resize(count); + a->camera_root.resize(count); + a3da_camera_root* vcr = a->camera_root.data(); for (int32_t i = 0; i < count; i++) { if (!kv.open_scope_fmt(i)) continue; @@ -693,9 +690,8 @@ static void a3da_read_text(a3da* a, void* data, size_t size) { } if (kv.read("chara", "length", count)) { - std::vector& vc = a->chara; - - vc.resize(count); + a->chara.resize(count); + a3da_chara* vc = a->chara.data(); for (int32_t i = 0; i < count; i++) { if (!kv.open_scope_fmt(i)) continue; @@ -710,9 +706,8 @@ static void a3da_read_text(a3da* a, void* data, size_t size) { } if (kv.read("curve", "length", count)) { - std::vector& vc = a->curve; - - vc.resize(count); + a->curve.resize(count); + a3da_curve* vc = a->curve.data(); for (int32_t i = 0; i < count; i++) { if (!kv.open_scope_fmt(i)) continue; @@ -727,9 +722,8 @@ static void a3da_read_text(a3da* a, void* data, size_t size) { } if (kv.read("event", "length", count)) { - std::vector& ve = a->event; - - ve.resize(count); + a->event.resize(count); + a3da_event* ve = a->event.data(); for (int32_t i = 0; i < count; i++) { if (!kv.open_scope_fmt(i)) continue; @@ -754,9 +748,8 @@ static void a3da_read_text(a3da* a, void* data, size_t size) { } if (kv.read("fog", "length", count)) { - std::vector& vf = a->fog; - - vf.resize(count); + a->fog.resize(count); + a3da_fog* vf = a->fog.data(); for (int32_t i = 0; i < count; i++) { if (!kv.open_scope_fmt(i)) continue; @@ -790,9 +783,8 @@ static void a3da_read_text(a3da* a, void* data, size_t size) { } if (kv.read("light", "length", count)) { - std::vector& vl = a->light; - - vl.resize(count); + a->light.resize(count); + a3da_light* vl = a->light.data(); for (int32_t i = 0; i < count; i++) { if (!kv.open_scope_fmt(i)) continue; @@ -837,9 +829,8 @@ static void a3da_read_text(a3da* a, void* data, size_t size) { } if (kv.read("m_objhrc", "length", count)) { - std::vector& vmoh = a->m_object_hrc; - - vmoh.resize(count); + a->m_object_hrc.resize(count); + a3da_m_object_hrc* vmoh = a->m_object_hrc.data(); for (int32_t i = 0; i < count; i++) { if (!kv.open_scope_fmt(i)) continue; @@ -847,9 +838,8 @@ static void a3da_read_text(a3da* a, void* data, size_t size) { a3da_m_object_hrc* moh = &vmoh[i]; int32_t count; if (kv.read("instance", "length", count)) { - std::vector& voi = moh->instance; - - voi.resize(count); + moh->instance.resize(count); + a3da_object_instance* voi = moh->instance.data(); for (int32_t j = 0; j < count; j++) { if (!kv.open_scope_fmt(j)) continue; @@ -869,9 +859,8 @@ static void a3da_read_text(a3da* a, void* data, size_t size) { kv.read("name", moh->name); if (kv.read("node", "length", count)) { - std::vector& von = moh->node; - - von.resize(count); + moh->node.resize(count); + a3da_object_node* von = moh->node.data(); for (int32_t j = 0; j < count; j++) { if (!kv.open_scope_fmt(j)) continue; @@ -894,9 +883,8 @@ static void a3da_read_text(a3da* a, void* data, size_t size) { } if (kv.read("m_objhrc_list", "length", count)) { - std::vector& vmohl = a->m_object_hrc_list; - - vmohl.resize(count); + a->m_object_hrc_list.resize(count); + std::string* vmohl = a->m_object_hrc_list.data(); for (int32_t i = 0; i < count; i++) { if (!kv.open_scope_fmt(i)) continue; @@ -908,9 +896,8 @@ static void a3da_read_text(a3da* a, void* data, size_t size) { } if (kv.read("material_list", "length", count)) { - std::vector& vml = a->material_list; - - vml.resize(count); + a->material_list.resize(count); + a3da_material_list* vml = a->material_list.data(); for (int32_t i = 0; i < count; i++) { if (!kv.open_scope_fmt(i)) continue; @@ -930,9 +917,8 @@ static void a3da_read_text(a3da* a, void* data, size_t size) { } if (kv.read("motion", "length", count)) { - std::vector& vm = a->motion; - - vm.resize(count); + a->motion.resize(count); + std::string* vm = a->motion.data(); for (int32_t i = 0; i < count; i++) { if (!kv.open_scope_fmt(i)) continue; @@ -944,9 +930,8 @@ static void a3da_read_text(a3da* a, void* data, size_t size) { } if (kv.read("object", "length", count)) { - std::vector& vo = a->object; - - vo.resize(count); + a->object.resize(count); + a3da_object* vo = a->object.data(); for (int32_t i = 0; i < count; i++) { if (!kv.open_scope_fmt(i)) continue; @@ -963,9 +948,8 @@ static void a3da_read_text(a3da* a, void* data, size_t size) { int32_t count; if (kv.read("tex_pat", "length", count)) { - std::vector& votp = o->texture_pattern; - - votp.resize(count); + o->texture_pattern.resize(count); + a3da_object_texture_pattern* votp = o->texture_pattern.data(); for (int32_t j = 0; j < count; j++) { if (!kv.open_scope_fmt(j)) continue; @@ -981,9 +965,8 @@ static void a3da_read_text(a3da* a, void* data, size_t size) { } if (kv.read("tex_transform", "length", count)) { - std::vector& vott = o->texture_transform; - - vott.resize(count); + o->texture_transform.resize(count); + a3da_object_texture_transform* vott = o->texture_transform.data(); for (int32_t j = 0; j < count; j++) { if (!kv.open_scope_fmt(j)) continue; @@ -1024,9 +1007,8 @@ static void a3da_read_text(a3da* a, void* data, size_t size) { } if (kv.read("object_list", "length", count)) { - std::vector& vol = a->object_list; - - vol.resize(count); + a->object_list.resize(count); + std::string* vol = a->object_list.data(); for (int32_t i = 0; i < count; i++) { if (!kv.open_scope_fmt(i)) continue; @@ -1038,9 +1020,8 @@ static void a3da_read_text(a3da* a, void* data, size_t size) { } if (kv.read("objhrc", "length", count)) { - std::vector& voh = a->object_hrc; - - voh.resize(count); + a->object_hrc.resize(count); + a3da_object_hrc* voh = a->object_hrc.data(); for (int32_t i = 0; i < count; i++) { if (!kv.open_scope_fmt(i)) continue; @@ -1050,9 +1031,8 @@ static void a3da_read_text(a3da* a, void* data, size_t size) { int32_t count; if (kv.read("node", "length", count)) { - std::vector& vohn = oh->node; - - vohn.resize(count); + oh->node.resize(count); + a3da_object_node* vohn = oh->node.data(); for (int32_t j = 0; j < count; j++) { if (!kv.open_scope_fmt(j)) continue; @@ -1080,9 +1060,8 @@ static void a3da_read_text(a3da* a, void* data, size_t size) { } if (kv.read("objhrc_list", "length", count)) { - std::vector& vohl = a->object_hrc_list; - - vohl.resize(count); + a->object_hrc_list.resize(count); + std::string* vohl = a->object_hrc_list.data(); for (int32_t i = 0; i < count; i++) { if (!kv.open_scope_fmt(i)) continue; @@ -1094,9 +1073,8 @@ static void a3da_read_text(a3da* a, void* data, size_t size) { } if (kv.read("point", "length", count)) { - std::vector& vp = a->point; - - vp.resize(count); + a->point.resize(count); + a3da_point* vp = a->point.data(); for (int32_t i = 0; i < count; i++) { if (!kv.open_scope_fmt(i)) continue; @@ -1144,14 +1122,16 @@ static void a3da_write_text(a3da* a, void** data, size_t* size, bool a3dc) { if (a->ambient.size() && a->format == A3DA_FORMAT_MGF) { kv.open_scope("ambient"); - std::vector& va = a->ambient; - int32_t count = (int32_t)va.size(); - std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); - for (int32_t i = 0; i < count; i++) { - kv.open_scope_fmt(sort_index[i]); + int32_t count = (int32_t)a->ambient.size(); + a3da_ambient* va = a->ambient.data(); - a3da_ambient* a = &va[sort_index[i]]; + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); + for (int32_t i = 0; i < count; i++) { + kv.open_scope_fmt(sort_index_data[i]); + + a3da_ambient* a = &va[sort_index_data[i]]; if (a->flags & A3DA_AMBIENT_LIGHT_DIFFUSE) key_val_out_write(&kv, s, "light.Diffuse", a->light_diffuse); kv.write(s, "name", a->name); @@ -1168,13 +1148,15 @@ static void a3da_write_text(a3da* a, void** data, size_t* size, bool a3dc) { if (a->auth_2d.size()) { kv.open_scope("auth_2d"); - std::vector& va2 = a->auth_2d; - int32_t count = (int32_t)va2.size(); + int32_t count = (int32_t)a->auth_2d.size(); + std::string* va2 = a->auth_2d.data(); + std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); for (int32_t i = 0; i < count; i++) { - kv.open_scope_fmt(sort_index[i]); - kv.write(s, "name", va2[sort_index[i]]); + kv.open_scope_fmt(sort_index_data[i]); + kv.write(s, "name", va2[sort_index_data[i]]); kv.close_scope(); } @@ -1211,14 +1193,16 @@ static void a3da_write_text(a3da* a, void** data, size_t* size, bool a3dc) { if (a->camera_root.size() > 0) { kv.open_scope("camera_root"); - std::vector& vcr = a->camera_root; - int32_t count = (int32_t)vcr.size(); - std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); - for (int32_t i = 0; i < count; i++) { - kv.open_scope_fmt(sort_index[i]); + int32_t count = (int32_t)a->camera_root.size(); + a3da_camera_root* vcr = a->camera_root.data(); - a3da_camera_root* cr = &vcr[sort_index[i]]; + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); + for (int32_t i = 0; i < count; i++) { + kv.open_scope_fmt(sort_index_data[i]); + + a3da_camera_root* cr = &vcr[sort_index_data[i]]; key_val_out_write(&kv, s, "interest", cr->interest, 0x1F); key_val_out_write(&kv, s, "", cr->model_transform, 0x1E); @@ -1256,14 +1240,16 @@ static void a3da_write_text(a3da* a, void** data, size_t* size, bool a3dc) { if (a->chara.size() > 0) { kv.open_scope("chara"); - std::vector& vc = a->chara; - int32_t count = (int32_t)vc.size(); - std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); - for (int32_t i = 0; i < count; i++) { - kv.open_scope_fmt(sort_index[i]); + int32_t count = (int32_t)a->chara.size(); + a3da_chara* vc = a->chara.data(); - a3da_chara* c = &vc[sort_index[i]]; + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); + for (int32_t i = 0; i < count; i++) { + kv.open_scope_fmt(sort_index_data[i]); + + a3da_chara* c = &vc[sort_index_data[i]]; kv.write(s, "name", c->name); key_val_out_write(&kv, s, "", c->model_transform); @@ -1277,14 +1263,16 @@ static void a3da_write_text(a3da* a, void** data, size_t* size, bool a3dc) { if (a->curve.size() > 0) { kv.open_scope("curve"); - std::vector& vc = a->curve; - int32_t count = (int32_t)vc.size(); - std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); - for (int32_t i = 0; i < count; i++) { - kv.open_scope_fmt(sort_index[i]); + int32_t count = (int32_t)a->curve.size(); + a3da_curve* vc = a->curve.data(); - a3da_curve* c = &vc[sort_index[i]]; + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); + for (int32_t i = 0; i < count; i++) { + kv.open_scope_fmt(sort_index_data[i]); + + a3da_curve* c = &vc[sort_index_data[i]]; key_val_out_write(&kv, s, "cv", c->curve); kv.write(s, "name", c->name); @@ -1308,14 +1296,16 @@ static void a3da_write_text(a3da* a, void** data, size_t* size, bool a3dc) { if (a->event.size() > 0) { kv.open_scope("event"); - std::vector& ve = a->event; - int32_t count = (int32_t)ve.size(); - std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); - for (int32_t i = 0; i < count; i++) { - kv.open_scope_fmt(sort_index[i]); + int32_t count = (int32_t)a->event.size(); + a3da_event* ve = a->event.data(); - a3da_event* e = &ve[sort_index[i]]; + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); + for (int32_t i = 0; i < count; i++) { + kv.open_scope_fmt(sort_index_data[i]); + + a3da_event* e = &ve[sort_index_data[i]]; kv.write(s, "begin", e->begin); kv.write(s, "clip_begin", e->clip_begin); kv.write(s, "clip_en", e->clip_end); @@ -1336,14 +1326,16 @@ static void a3da_write_text(a3da* a, void** data, size_t* size, bool a3dc) { if (a->fog.size() > 0) { kv.open_scope("fog"); - std::vector& vf = a->fog; - int32_t count = (int32_t)vf.size(); - std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); - for (int32_t i = 0; i < count; i++) { - kv.open_scope_fmt(sort_index[i]); + int32_t count = (int32_t)a->fog.size(); + a3da_fog* vf = a->fog.data(); - a3da_fog* f = &vf[sort_index[i]]; + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); + for (int32_t i = 0; i < count; i++) { + kv.open_scope_fmt(sort_index_data[i]); + + a3da_fog* f = &vf[sort_index_data[i]]; if (f->flags & A3DA_FOG_COLOR) key_val_out_write(&kv, s, "Diffuse", f->color); if (f->flags & A3DA_FOG_DENSITY) @@ -1366,14 +1358,16 @@ static void a3da_write_text(a3da* a, void** data, size_t* size, bool a3dc) { bool xhd = a->format == A3DA_FORMAT_XHD; - std::vector& vl = a->light; - int32_t count = (int32_t)vl.size(); - std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); - for (int32_t i = 0; i < count; i++) { - kv.open_scope_fmt(sort_index[i]); + int32_t count = (int32_t)a->light.size(); + a3da_light* vl = a->light.data(); - a3da_light* l = &vl[sort_index[i]]; + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); + for (int32_t i = 0; i < count; i++) { + kv.open_scope_fmt(sort_index_data[i]); + + a3da_light* l = &vl[sort_index_data[i]]; if (l->flags & A3DA_LIGHT_AMBIENT) key_val_out_write(&kv, s, "Ambient", l->ambient); if (l->flags & A3DA_LIGHT_CONSTANT && xhd) @@ -1435,25 +1429,29 @@ static void a3da_write_text(a3da* a, void** data, size_t* size, bool a3dc) { if (a->m_object_hrc.size() > 0) { kv.open_scope("m_objhrc"); - std::vector& vmoh = a->m_object_hrc; - int32_t count = (int32_t)vmoh.size(); - std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); - for (int32_t i = 0; i < count; i++) { - kv.open_scope_fmt(sort_index[i]); + int32_t count = (int32_t)a->m_object_hrc.size(); + a3da_m_object_hrc* vmoh = a->m_object_hrc.data(); - a3da_m_object_hrc* moh = &vmoh[sort_index[i]]; + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); + for (int32_t i = 0; i < count; i++) { + kv.open_scope_fmt(sort_index_data[i]); + + a3da_m_object_hrc* moh = &vmoh[sort_index_data[i]]; if (moh->instance.size()) { kv.open_scope("instance"); - std::vector& voi = moh->instance; - int32_t count = (int32_t)voi.size(); - std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); - for (int32_t j = 0; j < count; j++) { - kv.open_scope_fmt(sort_index[j]); + int32_t count = (int32_t)moh->instance.size(); + a3da_object_instance* voi = moh->instance.data(); - a3da_object_instance* oi = &voi[sort_index[j]]; + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); + for (int32_t j = 0; j < count; j++) { + kv.open_scope_fmt(sort_index_data[j]); + + a3da_object_instance* oi = &voi[sort_index_data[j]]; key_val_out_write(&kv, s, "", oi->model_transform, 0x10); kv.write(s, "name", oi->name); key_val_out_write(&kv, s, "", oi->model_transform, 0x0C); @@ -1475,14 +1473,16 @@ static void a3da_write_text(a3da* a, void** data, size_t* size, bool a3dc) { if (moh->node.size()) { kv.open_scope("node"); - std::vector& von = moh->node; - int32_t count = (int32_t)von.size(); - std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); - for (int32_t j = 0; j < count; j++) { - kv.open_scope_fmt(sort_index[j]); + int32_t count = (int32_t)moh->node.size(); + a3da_object_node* von = moh->node.data(); - a3da_object_node* on = &von[sort_index[j]]; + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); + for (int32_t j = 0; j < count; j++) { + kv.open_scope_fmt(sort_index_data[j]); + + a3da_object_node* on = &von[sort_index_data[j]]; key_val_out_write(&kv, s, "", on->model_transform, 0x10); if (on->flags & A3DA_OBJECT_NODE_JOINT_ORIENT) kv.write(s, "joint_orient", on->joint_orient); @@ -1509,13 +1509,15 @@ static void a3da_write_text(a3da* a, void** data, size_t* size, bool a3dc) { if (a->m_object_hrc_list.size()) { kv.open_scope("m_objhrc_list"); - std::vector& vmohl = a->m_object_hrc_list; - int32_t count = (int32_t)vmohl.size(); + int32_t count = (int32_t)a->m_object_hrc_list.size(); + std::string* vmohl = a->m_object_hrc_list.data(); + std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); for (int32_t i = 0; i < count; i++) { - kv.open_scope_fmt(sort_index[i]); - kv.write(s, "name", vmohl[sort_index[i]]); + kv.open_scope_fmt(sort_index_data[i]); + kv.write(s, "name", vmohl[sort_index_data[i]]); kv.close_scope(); } @@ -1526,14 +1528,16 @@ static void a3da_write_text(a3da* a, void** data, size_t* size, bool a3dc) { if (a->material_list.size() > 0 && (a->format == A3DA_FORMAT_X || a->format == A3DA_FORMAT_XHD)) { kv.open_scope("material_list"); - std::vector& vml = a->material_list; - int32_t count = (int32_t)vml.size(); - std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); - for (int32_t i = 0; i < count; i++) { - kv.open_scope_fmt(sort_index[i]); + int32_t count = (int32_t)a->material_list.size(); + a3da_material_list* vml = a->material_list.data(); - a3da_material_list* ml = &vml[sort_index[i]]; + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); + for (int32_t i = 0; i < count; i++) { + kv.open_scope_fmt(sort_index_data[i]); + + a3da_material_list* ml = &vml[sort_index_data[i]]; if (ml->flags & A3DA_MATERIAL_LIST_BLEND_COLOR) key_val_out_write(&kv, s, "blend_color", ml->blend_color); if (ml->flags & A3DA_MATERIAL_LIST_BLEND_COLOR) @@ -1553,13 +1557,15 @@ static void a3da_write_text(a3da* a, void** data, size_t* size, bool a3dc) { if (a->motion.size()) { kv.open_scope("motion"); - std::vector& vm = a->motion; - int32_t count = (int32_t)vm.size(); + int32_t count = (int32_t)a->motion.size(); + std::string* vm = a->motion.data(); + std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); for (int32_t i = 0; i < count; i++) { - kv.open_scope_fmt(sort_index[i]); - kv.write(s, "name", vm[sort_index[i]]); + kv.open_scope_fmt(sort_index_data[i]); + kv.write(s, "name", vm[sort_index_data[i]]); kv.close_scope(); } @@ -1570,14 +1576,16 @@ static void a3da_write_text(a3da* a, void** data, size_t* size, bool a3dc) { if (a->object.size() > 0) { kv.open_scope("object"); - std::vector& vo = a->object; - int32_t count = (int32_t)vo.size(); - std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); - for (int32_t i = 0; i < count; i++) { - kv.open_scope_fmt(sort_index[i]); + int32_t count = (int32_t)a->object.size(); + a3da_object* vo = a->object.data(); - a3da_object* o = &vo[sort_index[i]]; + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); + for (int32_t i = 0; i < count; i++) { + kv.open_scope_fmt(sort_index_data[i]); + + a3da_object* o = &vo[sort_index_data[i]]; key_val_out_write(&kv, s, "", o->model_transform, 0x10); if (o->morph.size()) { kv.write(s, "morph", o->morph); @@ -1595,14 +1603,16 @@ static void a3da_write_text(a3da* a, void** data, size_t* size, bool a3dc) { if (o->texture_pattern.size()) { kv.open_scope("tex_pat"); - std::vector& votp = o->texture_pattern; - int32_t count = (int32_t)votp.size(); - std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); - for (int32_t j = 0; j < count; j++) { - kv.open_scope_fmt(sort_index[j]); + int32_t count = (int32_t)o->texture_pattern.size(); + a3da_object_texture_pattern* votp = o->texture_pattern.data(); - a3da_object_texture_pattern* otp = &votp[sort_index[j]]; + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); + for (int32_t j = 0; j < count; j++) { + kv.open_scope_fmt(sort_index_data[j]); + + a3da_object_texture_pattern* otp = &votp[sort_index_data[j]]; kv.write(s, "name", otp->name); if (otp->pattern.size()) { kv.write(s, "pat", otp->pattern); @@ -1619,14 +1629,16 @@ static void a3da_write_text(a3da* a, void** data, size_t* size, bool a3dc) { if (o->texture_transform.size()) { kv.open_scope("tex_transform"); - std::vector& vott = o->texture_transform; - int32_t count = (int32_t)vott.size(); - std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); - for (int32_t j = 0; j < count; j++) { - kv.open_scope_fmt(sort_index[j]); + int32_t count = (int32_t)o->texture_transform.size(); + a3da_object_texture_transform* vott = o->texture_transform.data(); - a3da_object_texture_transform* ott = &vott[sort_index[j]]; + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); + for (int32_t j = 0; j < count; j++) { + kv.open_scope_fmt(sort_index_data[j]); + + a3da_object_texture_transform* ott = &vott[sort_index_data[j]]; if (ott->flags & A3DA_OBJECT_TEXTURE_TRANSFORM_COVERAGE_U) key_val_out_write(&kv, s, "coverageU", ott->coverage_u); if (ott->flags & A3DA_OBJECT_TEXTURE_TRANSFORM_COVERAGE_V) @@ -1670,13 +1682,15 @@ static void a3da_write_text(a3da* a, void** data, size_t* size, bool a3dc) { if (a->object_list.size()) { kv.open_scope("object_list"); - std::vector& vol = a->object_list; - int32_t count = (int32_t)vol.size(); + int32_t count = (int32_t)a->object_list.size(); + std::string* vol = a->object_list.data(); + std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); for (int32_t i = 0; i < count; i++) { - kv.open_scope_fmt(sort_index[i]); - kv.write(s, "name", vol[sort_index[i]]); + kv.open_scope_fmt(sort_index_data[i]); + kv.write(s, "name", vol[sort_index_data[i]]); kv.close_scope(); } @@ -1687,27 +1701,31 @@ static void a3da_write_text(a3da* a, void** data, size_t* size, bool a3dc) { if (a->object_hrc.size() > 0) { kv.open_scope("objhrc"); - std::vector& voh = a->object_hrc; - int32_t count = (int32_t)voh.size(); - std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); - for (int32_t i = 0; i < count; i++) { - kv.open_scope_fmt(sort_index[i]); + int32_t count = (int32_t)a->object_hrc.size(); + a3da_object_hrc* voh = a->object_hrc.data(); - a3da_object_hrc* oh = &voh[sort_index[i]]; + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); + for (int32_t i = 0; i < count; i++) { + kv.open_scope_fmt(sort_index_data[i]); + + a3da_object_hrc* oh = &voh[sort_index_data[i]]; kv.write(s, "name", oh->name); if (oh->node.size()) { kv.open_scope("node"); - std::vector& vohn = oh->node; - int32_t count = (int32_t)vohn.size(); - std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); - for (int32_t j = 0; j < count; j++) { - kv.open_scope_fmt(sort_index[j]); + int32_t count = (int32_t)oh->node.size(); + a3da_object_node* vohn = oh->node.data(); - a3da_object_node* ohn = &vohn[sort_index[j]]; + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); + for (int32_t j = 0; j < count; j++) { + kv.open_scope_fmt(sort_index_data[j]); + + a3da_object_node* ohn = &vohn[sort_index_data[j]]; key_val_out_write(&kv, s, "", ohn->model_transform, 0x10); if (ohn->flags & A3DA_OBJECT_NODE_JOINT_ORIENT) kv.write(s, "joint_orient", ohn->joint_orient); @@ -1737,13 +1755,15 @@ static void a3da_write_text(a3da* a, void** data, size_t* size, bool a3dc) { if (a->object_hrc_list.size()) { kv.open_scope("objhrc_list"); - std::vector& vohl = a->object_hrc_list; - int32_t count = (int32_t)vohl.size(); + int32_t count = (int32_t)a->object_hrc_list.size(); + std::string* vohl = a->object_hrc_list.data(); + std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); for (int32_t i = 0; i < count; i++) { - kv.open_scope_fmt(sort_index[i]); - kv.write(s, "name", vohl[sort_index[i]]); + kv.open_scope_fmt(sort_index_data[i]); + kv.write(s, "name", vohl[sort_index_data[i]]); kv.close_scope(); } @@ -1796,14 +1816,16 @@ static void a3da_write_text(a3da* a, void** data, size_t* size, bool a3dc) { if (a->point.size() > 0) { kv.open_scope("point"); - std::vector& vp = a->point; - int32_t count = (int32_t)vp.size(); - std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); - for (int32_t i = 0; i < count; i++) { - kv.open_scope_fmt(sort_index[i]); + int32_t count = (int32_t)a->point.size(); + a3da_point* vp = a->point.data(); - a3da_point* p = &vp[sort_index[i]]; + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); + for (int32_t i = 0; i < count; i++) { + kv.open_scope_fmt(sort_index_data[i]); + + a3da_point* p = &vp[sort_index_data[i]]; kv.write(s, "name", p->name); key_val_out_write(&kv, s, "", p->model_transform); @@ -2289,10 +2311,11 @@ static bool key_val_read(key_val* kv, return false; } - value.keys.reserve(length); + int32_t act_length = length; + value.keys.resize(length); + kft3* keys = value.keys.data(); - kft3 k = { 0.0f, 0.0f, 0.0f, 0.0f }; - for (int32_t i = 0; i < length; i++) { + for (int32_t i = 0, j = 0; i < length; i++) { if (!kv->open_scope_fmt(i)) continue; @@ -2307,29 +2330,20 @@ static bool key_val_read(key_val* kv, case KEY_FRAME_TYPE_0: { float_t f; if (sscanf_s(data, "%g", &f) == 1) - k = { f, 0.0f, 0.0f, 0.0f }; - else - k = { 0.0f, 0.0f, 0.0f, 0.0f }; - value.keys.push_back(k); + keys[j++] = { f, 0.0f, 0.0f, 0.0f }; } break; case KEY_FRAME_TYPE_1: { float_t f; float_t v; if (sscanf_s(data, "(%g,%g)", &f, &v) == 2) - k = { f, v, 0.0f, 0.0f }; - else - k = { 0.0f, 0.0f, 0.0f, 0.0f }; - value.keys.push_back(k); + keys[j++] = { f, v, 0.0f, 0.0f }; } break; case KEY_FRAME_TYPE_2: { float_t f; float_t v; float_t t; if (sscanf_s(data, "(%g,%g,%g)", &f, &v, &t) == 3) - k = { f, v, t, t }; - else - k = { 0.0f, 0.0f, 0.0f, 0.0f }; - value.keys.push_back(k); + keys[j++] = { f, v, t, t }; } break; case KEY_FRAME_TYPE_3: { float_t f; @@ -2337,19 +2351,16 @@ static bool key_val_read(key_val* kv, float_t t1; float_t t2; if (sscanf_s(data, "(%g,%g,%g,%g)", &f, &v, &t1, &t2) == 4) - k = { f, v, t1, t2 }; - else - k = { 0.0f, 0.0f, 0.0f, 0.0f }; - value.keys.push_back(k); - } break; - default: { - k = { 0.0f, 0.0f, 0.0f, 0.0f }; - value.keys.push_back(k); + keys[j++] = { f, v, t1, t2 }; } break; + default: + keys[j++] = {}; + break; } kv->close_scope(); } + value.keys.resize(act_length); kv->close_scope(); kv->close_scope(); @@ -2395,11 +2406,12 @@ static void key_val_out_write(key_val_out* kv, stream& s, int32_t length = (int32_t)value.keys.size(); std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, length); + key_val_out::get_lexicographic_order(sort_index, length); + int32_t* sort_index_data = sort_index.data(); for (int32_t i = 0; i < length; i++) { - kv->open_scope_fmt(sort_index[i]); + kv->open_scope_fmt(sort_index_data[i]); - kft3 k = value.keys[sort_index[i]]; + kft3 k = value.keys[sort_index_data[i]]; kf_type kt = KEY_FRAME_TYPE_3; kft_check(&k, kt, &k, &kt); @@ -2494,48 +2506,34 @@ static bool key_val_read_raw_data(key_val* kv, switch (key_type) { case 0: { - kft3 k = { 0.0f, 0.0f, 0.0f, 0.0f }; - value.keys.reserve(c); - for (size_t i = 0; i < c; i++) { - k.frame = *fs++; - value.keys.push_back(k); - } + value.keys.resize(c); + kft3* keys = value.keys.data(); + for (size_t i = 0; i < c; i++, fs++) + keys[i] = { fs[0], 0.0f, 0.0f, 0.0f }; fs -= c; } break; case 1: { - kft3 k = { 0.0f, 0.0f, 0.0f, 0.0f }; c /= 2; - value.keys.reserve(c); - for (size_t i = 0; i < c; i++) { - k.frame = *fs++; - k.value = *fs++; - value.keys.push_back(k); - } + value.keys.resize(c); + kft3* keys = value.keys.data(); + for (size_t i = 0; i < c; i++, fs += 2) + keys[i] = { fs[0], fs[1], 0.0f, 0.0f }; fs -= c * 2; } break; case 2: { - kft3 k = { 0.0f, 0.0f, 0.0f, 0.0f }; c /= 3; - value.keys.reserve(c); - for (size_t i = 0; i < c; i++) { - k.frame = *fs++; - k.value = *fs++; - k.tangent1 = k.tangent2 = *fs++; - value.keys.push_back(k); - } + value.keys.resize(c); + kft3* keys = value.keys.data(); + for (size_t i = 0; i < c; i++, fs += 3) + keys[i] = { fs[0], fs[1], fs[2], fs[2] }; fs -= c * 3; } break; case 3: { - kft3 k = { 0.0f, 0.0f, 0.0f, 0.0f }; c /= 4; - value.keys.reserve(c); - for (size_t i = 0; i < c; i++) { - k.frame = *fs++; - k.value = *fs++; - k.tangent1 = *fs++; - k.tangent2 = *fs++; - value.keys.push_back(k); - } + value.keys.resize(c); + kft3* keys = value.keys.data(); + for (size_t i = 0; i < c; i++, fs += 4) + keys[i] = { fs[0], fs[1], fs[2], fs[3] }; fs -= c * 4; } break; default: @@ -2564,9 +2562,11 @@ static void key_val_out_write_raw_data(key_val_out* kv, stream& s, return; } + kft3* keys = value.keys.data(); + kf_type key_type = KEY_FRAME_TYPE_0; for (int32_t i = 0; i < length; i++) { - kft3 k = value.keys[i]; + kft3 k = keys[i]; kf_type kt = KEY_FRAME_TYPE_3; kft_check(&k, kt, &k, &kt); if (key_type < kt) @@ -2584,7 +2584,7 @@ static void key_val_out_write_raw_data(key_val_out* kv, stream& s, switch (key_type) { case KEY_FRAME_TYPE_0: for (int32_t i = 0; i < length; i++) { - kft3 k = value.keys[i]; + kft3 k = keys[i]; sprintf_s(data_buf, sizeof(data_buf), "%g", k.frame); s.write_utf8_string(data_buf); @@ -2594,7 +2594,7 @@ static void key_val_out_write_raw_data(key_val_out* kv, stream& s, break; case KEY_FRAME_TYPE_1: for (int32_t i = 0; i < length; i++) { - kft3 k = value.keys[i]; + kft3 k = keys[i]; sprintf_s(data_buf, sizeof(data_buf), "%g,%g", k.frame, k.value); s.write_utf8_string(data_buf); @@ -2604,7 +2604,7 @@ static void key_val_out_write_raw_data(key_val_out* kv, stream& s, break; case KEY_FRAME_TYPE_2: for (int32_t i = 0; i < length; i++) { - kft3 k = value.keys[i]; + kft3 k = keys[i]; sprintf_s(data_buf, sizeof(data_buf), "%g,%g,%g", k.frame, k.value, k.tangent1); s.write_utf8_string(data_buf); @@ -2614,7 +2614,7 @@ static void key_val_out_write_raw_data(key_val_out* kv, stream& s, break; case KEY_FRAME_TYPE_3: for (int32_t i = 0; i < length; i++) { - kft3 k = value.keys[i]; + kft3 k = keys[i]; sprintf_s(data_buf, sizeof(data_buf), "%g,%g,%g,%g", k.frame, k.value, k.tangent1, k.tangent2); s.write_utf8_string(data_buf); @@ -2757,14 +2757,14 @@ static void a3dc_read_a3da_key_f16(void* data, size_t size, a3da_key* value, a3d value->raw_data_value_list_offset = 0; int32_t len = value->raw_data_value_list_size / 4; - value->keys.reserve(size); + value->keys.resize(size); + kft3* keys = value->keys.data(); for (int32_t i = 0; i < len; i++) { - kft3 k; + kft3& k = keys[i]; k.frame = *(float_t*)_d; k.value = *(float_t*)(_d + 4); k.tangent1 = *(float_t*)(_d + 8); k.tangent2 = *(float_t*)(_d + 12); - value->keys.push_back(k); _d += 16; } return; @@ -2794,38 +2794,37 @@ static void a3dc_read_a3da_key_f16(void* data, size_t size, a3da_key* value, a3d value->max_frame = head->max_frame; uint32_t len = head->length; - value->keys.reserve(len); + value->keys.resize(len); + kft3* keys = value->keys.data(); switch (f16) { case A3DA_COMPRESS_F32F32F32F32: + default: for (uint32_t i = 0; i < len; i++) { - kft3 k; + kft3& k = keys[i]; k.frame = *(float_t*)d; k.value = *(float_t*)(d + 4); k.tangent1 = *(float_t*)(d + 8); k.tangent2 = *(float_t*)(d + 12); - value->keys.push_back(k); d += 16; } break; case A3DA_COMPRESS_I16F16F32F32: for (uint32_t i = 0; i < len; i++) { - kft3 k; + kft3& k = keys[i]; k.frame = (float_t)*(int16_t*)d; k.value = half_to_float(*(half_t*)(d + 2)); k.tangent1 = *(float_t*)(d + 4); k.tangent2 = *(float_t*)(d + 8); - value->keys.push_back(k); d += 12; } break; case A3DA_COMPRESS_I16F16F16F16: for (uint32_t i = 0; i < len; i++) { - kft3 k; + kft3& k = keys[i]; k.frame = (float_t)*(int16_t*)d; k.value = half_to_float(*(half_t*)(d + 2)); k.tangent1 = half_to_float(*(half_t*)(d + 4)); k.tangent2 = half_to_float(*(half_t*)(d + 6)); - value->keys.push_back(k); d += 8; } break; @@ -2856,9 +2855,10 @@ static void a3dc_write_a3da_key_f16(stream& s, a3da_key& value, a3da_compress_f1 value.raw_data_value_list_offset = (int32_t)s.get_position(); int32_t len = (int32_t)value.keys.size(); + kft3* keys = value.keys.data(); value.raw_data_value_list_size = len * 4; for (int32_t i = 0; i < len; i++) { - kft3* k = &value.keys[i]; + kft3* k = &keys[i]; s.write_float_t(k->frame); s.write_float_t(k->value); s.write_float_t(k->tangent1); @@ -2882,6 +2882,7 @@ static void a3dc_write_a3da_key_f16(stream& s, a3da_key& value, a3da_compress_f1 } uint32_t len = (uint32_t)value.keys.size(); + kft3* keys = value.keys.data(); a3dc_key_header head = {}; head.type = value.type; @@ -2893,8 +2894,9 @@ static void a3dc_write_a3da_key_f16(stream& s, a3da_key& value, a3da_compress_f1 switch (f16) { case A3DA_COMPRESS_F32F32F32F32: + default: for (uint32_t i = 0; i < len; i++) { - kft3* k = &value.keys[i]; + kft3* k = &keys[i]; s.write_float_t(k->frame); s.write_float_t(k->value); s.write_float_t(k->tangent1); @@ -2903,7 +2905,7 @@ static void a3dc_write_a3da_key_f16(stream& s, a3da_key& value, a3da_compress_f1 break; case A3DA_COMPRESS_I16F16F32F32: for (uint32_t i = 0; i < len; i++) { - kft3* k = &value.keys[i]; + kft3* k = &keys[i]; s.write_int16_t((int16_t)roundf(k->frame)); s.write_half_t(float_to_half(k->value)); s.write_float_t(k->tangent1); @@ -2912,7 +2914,7 @@ static void a3dc_write_a3da_key_f16(stream& s, a3da_key& value, a3da_compress_f1 break; case A3DA_COMPRESS_I16F16F16F16: for (uint32_t i = 0; i < len; i++) { - kft3* k = &value.keys[i]; + kft3* k = &keys[i]; s.write_int16_t((int16_t)roundf(k->frame)); s.write_half_t(float_to_half(k->value)); s.write_half_t(float_to_half(k->tangent1)); diff --git a/src/KKdLib/alloc_data.cpp b/src/KKdLib/alloc_data.cpp deleted file mode 100644 index e1fc3c45..00000000 --- a/src/KKdLib/alloc_data.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/* - by korenkonder - GitHub/GitLab: korenkonder -*/ - -#include "alloc_data.hpp" - -alloc_data::alloc_data() : next() { - size = 4000; -} - -alloc_data::~alloc_data() { - deallocate(); -} - -void* alloc_data::allocate(size_t size) { - alloc_node* node = next; - size = max_def(size, 1); - size = (size + 7) / 8 * 8; - - bool allocate = true; - if (node) { - alloc_node* last_node = 0; - while (node) { - if ((size_t)(node->capacity - node->size) >= size) - last_node = node; - node = node->next; - } - - if (last_node) { - node = last_node; - allocate = false; - } - else - node = next; - } - - if (allocate) { - size_t data_size = this->size; - size_t _size = size + sizeof(alloc_node); - size_t mults = 0; - while (data_size < _size) { - data_size *= 2; - if (data_size >= _size) - break; - - mults++; - data_size *= 2; - - if (mults >= 16) { - if (data_size < _size) - return 0; - break; - } - } - - alloc_node* new_node = (alloc_node*)malloc(data_size); - if (!new_node) - return 0; - - next = new_node; - - new_node->next = node; - new_node->size = 0; - new_node->capacity = data_size - sizeof(alloc_node); - node = new_node; - } - - uint8_t* data = node->data + node->size; - memset(data, 0, size); - node->size += size; - return data; -} - -void alloc_data::deallocate() { - alloc_node* node = next; - while (node) { - alloc_node* next_node = node->next; - free(node); - node = next_node; - } - - next = 0; -} diff --git a/src/KKdLib/alloc_data.hpp b/src/KKdLib/alloc_data.hpp deleted file mode 100644 index dc1fbfdf..00000000 --- a/src/KKdLib/alloc_data.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - by korenkonder - GitHub/GitLab: korenkonder -*/ - -#pragma once - -#include "default.hpp" - -struct alloc_node { - alloc_node* next; - size_t size; - size_t capacity; -#pragma warning(suppress: 4200) - uint8_t data[]; -}; - -struct alloc_data { - size_t size; - alloc_node* next; - - alloc_data(); - ~alloc_data(); - - void* allocate(size_t size); - void deallocate(); - - template - inline T* allocate() { - return new((T*)allocate(sizeof(T))) T; - } - - template - inline T* allocate(size_t size) { - if (!size) - return 0; - - T* arr = (T*)allocate(sizeof(T) * size); - for (size_t i = 0; i < size; i++) - new(&arr[i]) T(); - return arr; - } - - template - inline T* allocate(const T* src) { - if (!src) - return 0; - - return new((T*)allocate(sizeof(T))) T(*src); - } - - template - inline T* allocate(const T* src, size_t size) { - if (!src || !size) - return 0; - - T* dst = (T*)allocate(sizeof(T) * size); - for (size_t i = 0; i < size; i++) - new(&dst[i]) T(src[i]); - return dst; - } -}; diff --git a/src/KKdLib/database/auth_3d.cpp b/src/KKdLib/database/auth_3d.cpp index 66270702..9b606bc1 100644 --- a/src/KKdLib/database/auth_3d.cpp +++ b/src/KKdLib/database/auth_3d.cpp @@ -7,6 +7,7 @@ #include "../io/file_stream.hpp" #include "../io/memory_stream.hpp" #include "../io/path.hpp" +#include "../prj/algorithm.hpp" #include "../key_val.hpp" #include "../hash.hpp" #include "../sort.hpp" @@ -22,6 +23,31 @@ static void auth_3d_database_file_write_inner(auth_3d_database_file* auth_3d_db_ static void auth_3d_database_file_read_text(auth_3d_database_file* auth_3d_db_file, void* data, size_t size); static void auth_3d_database_file_write_text(auth_3d_database_file* auth_3d_db_file, void** data, size_t* size); +auth_3d_database_uid::auth_3d_database_uid() : enabled(), org_uid(), size() { + category_hash = hash_murmurhash_empty; + name_hash = hash_murmurhash_empty; +} + +auth_3d_database_uid::~auth_3d_database_uid() { + +} + +auth_3d_database_uid_file::auth_3d_database_uid_file() : flags(), org_uid(), size() { + +} + +auth_3d_database_uid_file::~auth_3d_database_uid_file() { + +} + +auth_3d_database_category::auth_3d_database_category() { + name_hash = hash_murmurhash_empty; +} + +auth_3d_database_category::~auth_3d_database_category() { + +} + auth_3d_database_file::auth_3d_database_file() : ready(), uid_max(-1) { } @@ -134,58 +160,33 @@ void auth_3d_database::add(auth_3d_database_file* auth_3d_db_file, bool mdata) { auth_3d_database_load_uids(this, auth_3d_db_file, mdata); } -int32_t auth_3d_database::get_category_index(const char* name) { +int32_t auth_3d_database::get_category_index(const char* name) const { uint32_t name_hash = hash_utf8_murmurhash(name); - for (auth_3d_database_category i : category) + for (const auth_3d_database_category i : category) if (i.name_hash == name_hash) return (int32_t)(&i - category.data()); return -1; } -void auth_3d_database::get_category_uids(const char* name, std::vector& uid) { +void auth_3d_database::get_category_uids(const char* name, std::vector& uid) const { uid.clear(); uint32_t name_hash = hash_utf8_murmurhash(name); - for (auth_3d_database_category& i : category) + for (const auth_3d_database_category& i : category) if (i.name_hash == name_hash) { uid = i.uid; return; } } -int32_t auth_3d_database::get_uid(const char* name) { +int32_t auth_3d_database::get_uid(const char* name) const { uint32_t name_hash = hash_utf8_murmurhash(name); - for (auth_3d_database_uid& i : uid) + for (const auth_3d_database_uid& i : uid) if (i.name_hash == name_hash) return (int32_t)(&i - uid.data()); return -1; } -auth_3d_database_uid::auth_3d_database_uid() : enabled(), org_uid(), size() { - category_hash = hash_murmurhash_empty; - name_hash = hash_murmurhash_empty; -} - -auth_3d_database_uid::~auth_3d_database_uid() { - -} - -auth_3d_database_uid_file::auth_3d_database_uid_file() : flags(), org_uid(), size() { - -} - -auth_3d_database_uid_file::~auth_3d_database_uid_file() { - -} - -auth_3d_database_category::auth_3d_database_category() { - name_hash = hash_murmurhash_empty; -} - -auth_3d_database_category::~auth_3d_database_category() { - -} - static void auth_3d_database_load_categories(auth_3d_database* auth_3d_db, auth_3d_database_file* auth_3d_db_file, bool mdata) { std::vector& category = auth_3d_db->category; @@ -196,11 +197,11 @@ static void auth_3d_database_load_categories(auth_3d_database* auth_3d_db, category.reserve(count_file); for (int32_t i = 0; i < count_file; i++) { - uint32_t name_hash = hash_string_murmurhash(category_file[i]); + std::string& cat_file = category_file[i]; auth_3d_database_category* cat = 0; for (auth_3d_database_category& j : category) - if (name_hash == j.name_hash) { + if (!cat_file.compare(j.name)) { cat = &j; break; } @@ -265,22 +266,11 @@ static void auth_3d_database_load_uids(auth_3d_database* auth_3d_db, uid_file->category = uid->category; if (uid->category.size() > 0) { - uint32_t category_hash = hash_string_murmurhash(uid->category); + std::string& cat_file = uid->category; - for (auth_3d_database_category& j : category) { - if (category_hash != j.name_hash) - continue; - - bool found = false; - for (int32_t& k : j.uid) - if (k == org_uid) { - found = true; - break; - } - - if (!found) + for (auth_3d_database_category& j : category) + if (!cat_file.compare(j.name)) j.uid.push_back(org_uid); - } } } @@ -289,6 +279,11 @@ static void auth_3d_database_load_uids(auth_3d_database* auth_3d_db, else uid->size = -1.0f; } + + for (auth_3d_database_category& i : category) { + prj::sort(i.uid); + prj::unique(i.uid); + } } static void auth_3d_database_file_read_inner(auth_3d_database_file* auth_3d_db_file, stream& s) { @@ -327,9 +322,8 @@ static void auth_3d_database_file_read_text(auth_3d_database_file* auth_3d_db_fi auth_3d_db_file->category.clear(); int32_t count; if (kv.read("category", "length", count)) { - std::vector& vc = auth_3d_db_file->category; - - vc.resize(count); + auth_3d_db_file->category.resize(count); + std::string* vc = auth_3d_db_file->category.data(); for (int32_t i = 0; i < count; i++) { if (!kv.open_scope_fmt(i)) continue; @@ -347,9 +341,8 @@ static void auth_3d_database_file_read_text(auth_3d_database_file* auth_3d_db_fi int32_t count; if (kv.read("length", count)) { - std::vector& vu = auth_3d_db_file->uid; - - vu.resize(count); + auth_3d_db_file->uid.resize(count); + auth_3d_database_uid_file* vu = auth_3d_db_file->uid.data(); for (int32_t i = 0; i < count; i++) { if (!kv.open_scope_fmt(i)) continue; @@ -380,13 +373,15 @@ static void auth_3d_database_file_write_text(auth_3d_database_file* auth_3d_db_f if (auth_3d_db_file->category.size() > 0) { kv.open_scope("category"); - std::vector& vc = auth_3d_db_file->category; - int32_t count = (int32_t)vc.size(); + int32_t count = (int32_t)auth_3d_db_file->category.size(); + std::string* vc = auth_3d_db_file->category.data(); + std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); for (int32_t i = 0; i < count; i++) { - kv.open_scope_fmt(sort_index[i]); - kv.write(s, "value", vc[sort_index[i]]); + kv.open_scope_fmt(sort_index_data[i]); + kv.write(s, "value", vc[sort_index_data[i]]); kv.close_scope(); } @@ -398,14 +393,16 @@ static void auth_3d_database_file_write_text(auth_3d_database_file* auth_3d_db_f kv.open_scope("uid"); int32_t uid_max = -1; - std::vector& vu = auth_3d_db_file->uid; - int32_t count = (int32_t)vu.size(); - std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); - for (int32_t i = 0; i < count; i++) { - kv.open_scope_fmt(sort_index[i]); + int32_t count = (int32_t)auth_3d_db_file->uid.size(); + auth_3d_database_uid_file* vu = auth_3d_db_file->uid.data(); - auth_3d_database_uid_file* u = &vu[sort_index[i]]; + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); + for (int32_t i = 0; i < count; i++) { + kv.open_scope_fmt(sort_index_data[i]); + + auth_3d_database_uid_file* u = &vu[sort_index_data[i]]; kv.write(s, "category", u->category); if ((int32_t)(u->flags & AUTH_3D_DATABASE_UID_ORG_UID)) diff --git a/src/KKdLib/database/auth_3d.hpp b/src/KKdLib/database/auth_3d.hpp index 33d15305..e04bc293 100644 --- a/src/KKdLib/database/auth_3d.hpp +++ b/src/KKdLib/database/auth_3d.hpp @@ -75,7 +75,8 @@ struct auth_3d_database { ~auth_3d_database(); void add(auth_3d_database_file* auth_3d_db_file, bool mdata); - int32_t get_category_index(const char* name); - void get_category_uids(const char* name, std::vector& uid); - int32_t get_uid(const char* name); + + int32_t get_category_index(const char* name) const; + void get_category_uids(const char* name, std::vector& uid) const; + int32_t get_uid(const char* name) const; }; diff --git a/src/KKdLib/database/bone.cpp b/src/KKdLib/database/bone.cpp index b910a321..c44a98ae 100644 --- a/src/KKdLib/database/bone.cpp +++ b/src/KKdLib/database/bone.cpp @@ -29,6 +29,23 @@ static int64_t bone_database_strings_get_string_offset(std::vector& std::vector& vec_off, const char* str); static bool bone_database_strings_push_back_check(std::vector& vec, const char* str); +bone_database_bone::bone_database_bone() : type(), +has_parent(), parent(), pole_target(), mirror(), disable_mot_anim() { + +} + +bone_database_bone::~bone_database_bone() { + +} + +bone_database_skeleton::bone_database_skeleton() : heel_height() { + +} + +bone_database_skeleton::~bone_database_skeleton() { + +} + bone_database::bone_database() : ready(), modern(), big_endian(), is_x() { } @@ -188,7 +205,8 @@ void bone_database::write(void** data, size_t* size) { s.copy(data, size); } -bool bone_database::get_skeleton(const char* name, bone_database_skeleton** skeleton) { +bool bone_database::get_skeleton(const char* name, + const bone_database_skeleton** skeleton) const { if (!skeleton) return false; @@ -197,7 +215,7 @@ bool bone_database::get_skeleton(const char* name, bone_database_skeleton** skel return false; uint32_t name_hash = hash_utf8_murmurhash(name); - for (bone_database_skeleton& i : this->skeleton) + for (const bone_database_skeleton& i : this->skeleton) if (hash_string_murmurhash(i.name) == name_hash) { *skeleton = &i; return true; @@ -205,15 +223,15 @@ bool bone_database::get_skeleton(const char* name, bone_database_skeleton** skel return false; } -int32_t bone_database::get_skeleton_bone_index(const char* name, const char* bone_name) { +int32_t bone_database::get_skeleton_bone_index(const char* name, const char* bone_name) const { if (!name || !bone_name) return -1; uint32_t name_hash = hash_utf8_murmurhash(name); uint32_t bone_name_hash = hash_utf8_murmurhash(bone_name); - for (bone_database_skeleton& i : skeleton) + for (const bone_database_skeleton& i : skeleton) if (hash_string_murmurhash(i.name) == name_hash) { - for (bone_database_bone& j : i.bone) + for (const bone_database_bone& j : i.bone) if (hash_string_murmurhash(j.name) == bone_name_hash) return (int32_t)(&j - i.bone.data()); return -1; @@ -221,7 +239,8 @@ int32_t bone_database::get_skeleton_bone_index(const char* name, const char* bon return -1; } -bool bone_database::get_skeleton_bones(const char* name, std::vector** bone) { +bool bone_database::get_skeleton_bones(const char* name, + const std::vector** bone) const { if (!bone) return false; @@ -230,7 +249,7 @@ bool bone_database::get_skeleton_bones(const char* name, std::vector** positions) { +bool bone_database::get_skeleton_positions(const char* name, const std::vector** positions)const { if (!positions) return false; @@ -247,7 +266,7 @@ bool bone_database::get_skeleton_positions(const char* name, std::vector** return false; uint32_t name_hash = hash_utf8_murmurhash(name); - for (bone_database_skeleton& i : skeleton) + for (const bone_database_skeleton& i : skeleton) if (hash_string_murmurhash(i.name) == name_hash) { *positions = &i.position; return true; @@ -255,15 +274,15 @@ bool bone_database::get_skeleton_positions(const char* name, std::vector** return false; } -int32_t bone_database::get_skeleton_object_bone_index(const char* name, const char* bone_name) { +int32_t bone_database::get_skeleton_object_bone_index(const char* name, const char* bone_name) const { if (!name || !bone_name) return -1; uint32_t name_hash = hash_utf8_murmurhash(name); uint32_t bone_name_hash = hash_utf8_murmurhash(bone_name); - for (bone_database_skeleton& i : skeleton) + for (const bone_database_skeleton& i : skeleton) if (hash_string_murmurhash(i.name) == name_hash) { - for (std::string& j : i.object_bone) + for (const std::string& j : i.object_bone) if (hash_string_murmurhash(j) == bone_name_hash) return (int32_t)(&j - i.object_bone.data()); return -1; @@ -271,7 +290,8 @@ int32_t bone_database::get_skeleton_object_bone_index(const char* name, const ch return -1; } -bool bone_database::get_skeleton_object_bones(const char* name, std::vector** object_bones) { +bool bone_database::get_skeleton_object_bones(const char* name, + const std::vector** object_bones) const { if (!object_bones) return false; @@ -280,7 +300,7 @@ bool bone_database::get_skeleton_object_bones(const char* name, std::vector** motion_bones) { +bool bone_database::get_skeleton_motion_bones(const char* name, + const std::vector** motion_bones) const { if (!motion_bones) return false; @@ -313,7 +334,7 @@ bool bone_database::get_skeleton_motion_bones(const char* name, std::vector** parent_indices) { +bool bone_database::get_skeleton_parent_indices(const char* name, + const std::vector** parent_indices) const { if (!parent_indices) return false; @@ -330,7 +352,7 @@ bool bone_database::get_skeleton_parent_indices(const char* name, std::vectorready; } -void bone_database_bones_calculate_count(std::vector* bones, size_t* object_bone_count, - size_t* motion_bone_count, size_t* total_bone_count, size_t* ik_bone_count, size_t* chain_pos) { +void bone_database_bones_calculate_count(const std::vector* bones, + size_t* object_bone_count, size_t* motion_bone_count, + size_t* total_bone_count, size_t* ik_bone_count, size_t* chain_pos) { size_t _object_bone_count = 0; size_t _motion_bone_count = 0; size_t _total_bone_count = 0; size_t _ik_bone_count = 0; size_t _chain_pos = 0; - for (bone_database_bone& i : *bones) + for (const bone_database_bone& i : *bones) switch (i.type) { case BONE_DATABASE_BONE_ROTATION: case BONE_DATABASE_BONE_TYPE_1: @@ -448,23 +471,6 @@ const char* bone_database_skeleton_type_to_string(bone_database_skeleton_type ty } } -bone_database_bone::bone_database_bone() : type(), -has_parent(), parent(), pole_target(), mirror(), disable_mot_anim() { - -} - -bone_database_bone::~bone_database_bone() { - -} - -bone_database_skeleton::bone_database_skeleton() : heel_height() { - -} - -bone_database_skeleton::~bone_database_skeleton() { - -} - static void bone_database_classic_read_inner(bone_database* bone_data, stream& s) { uint32_t signature = s.read_uint32_t(); if (signature != 0x09102720) diff --git a/src/KKdLib/database/bone.hpp b/src/KKdLib/database/bone.hpp index 78e75631..58721254 100644 --- a/src/KKdLib/database/bone.hpp +++ b/src/KKdLib/database/bone.hpp @@ -79,21 +79,28 @@ struct bone_database { void write(const wchar_t* path); void write(void** data, size_t* size); - bool get_skeleton(const char* name, bone_database_skeleton** skeleton); - int32_t get_skeleton_bone_index(const char* name, const char* bone_name); - bool get_skeleton_bones(const char* name, std::vector** bone); - bool get_skeleton_positions(const char* name, std::vector** positions); - int32_t get_skeleton_object_bone_index(const char* name, const char* bone_name); - bool get_skeleton_object_bones(const char* name, std::vector** object_bones); - int32_t get_skeleton_motion_bone_index(const char* name, const char* bone_name); - bool get_skeleton_motion_bones(const char* name, std::vector** motion_bones); - bool get_skeleton_parent_indices(const char* name, std::vector** parent_indices); - bool get_skeleton_heel_height(const char* name, float_t** unknown_value); + bool get_skeleton(const char* name, + const bone_database_skeleton** skeleton) const; + int32_t get_skeleton_bone_index(const char* name, const char* bone_name) const; + bool get_skeleton_bones(const char* name, + const std::vector** bone) const; + bool get_skeleton_positions(const char* name, + const std::vector** positions) const; + int32_t get_skeleton_object_bone_index(const char* name, const char* bone_name) const; + bool get_skeleton_object_bones(const char* name, + const std::vector** object_bones) const; + int32_t get_skeleton_motion_bone_index(const char* name, const char* bone_name) const; + bool get_skeleton_motion_bones(const char* name, + const std::vector** motion_bones) const; + bool get_skeleton_parent_indices(const char* name, + const std::vector** parent_indices) const; + bool get_skeleton_heel_height(const char* name, const float_t** unknown_value) const; static bool load_file(void* data, const char* path, const char* file, uint32_t hash); }; -extern void bone_database_bones_calculate_count(std::vector* bones, size_t* object_bone_count, - size_t* motion_bone_count, size_t* total_bone_count, size_t* ik_bone_count, size_t* chain_pos); +extern void bone_database_bones_calculate_count(const std::vector* bones, + size_t* object_bone_count, size_t* motion_bone_count, + size_t* total_bone_count, size_t* ik_bone_count, size_t* chain_pos); extern const char* bone_database_skeleton_type_to_string(bone_database_skeleton_type type); diff --git a/src/KKdLib/database/hand_item.cpp b/src/KKdLib/database/hand_item.cpp new file mode 100644 index 00000000..fd45d6f4 --- /dev/null +++ b/src/KKdLib/database/hand_item.cpp @@ -0,0 +1,206 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#include "hand_item.hpp" +#include "../io/file_stream.hpp" +#include "../io/memory_stream.hpp" +#include "../io/path.hpp" +#include "../key_val.hpp" +#include "../sort.hpp" +#include "../str_utils.hpp" + +static void hnd_itm_read_inner(hnd_itm* itm_tbl, stream& s); +static void hnd_itm_write_inner(hnd_itm* itm_tbl, stream& s); +static void hnd_itm_read_text(hnd_itm* itm_tbl, void* data, size_t size); +static void hnd_itm_write_text(hnd_itm* itm_tbl, void** data, size_t* size); + +hnd_itm_data::hnd_itm_data() : file_size(), uid() { + hand_scale = -1.0f; +} + +hnd_itm_data::~hnd_itm_data() { + +} + +hnd_itm::hnd_itm() : ready() { + +} + +hnd_itm::~hnd_itm() { + +} + +void hnd_itm::read(const char* path) { + if (!path) + return; + + char* path_bin = str_utils_add(path, ".txt"); + if (path_check_file_exists(path_bin)) { + file_stream s; + s.open(path_bin, "rb"); + if (s.check_not_null()) + hnd_itm_read_inner(this, s); + } + free_def(path_bin); +} + +void hnd_itm::read(const wchar_t* path) { + if (!path) + return; + + wchar_t* path_bin = str_utils_add(path, L".txt"); + if (path_check_file_exists(path_bin)) { + file_stream s; + s.open(path_bin, L"rb"); + if (s.check_not_null()) + hnd_itm_read_inner(this, s); + } + free_def(path_bin); +} + +void hnd_itm::read(const void* data, size_t size) { + if (!data || !size) + return; + + memory_stream s; + s.open(data, size); + hnd_itm_read_inner(this, s); +} + +void hnd_itm::write(const char* path) { + if (!path || !ready) + return; + + char* path_bin = str_utils_add(path, ".txt"); + file_stream s; + s.open(path_bin, "wb"); + if (s.check_not_null()) + hnd_itm_write_inner(this, s); + free_def(path_bin); +} + +void hnd_itm::write(const wchar_t* path) { + if (!path || !ready) + return; + + wchar_t* path_bin = str_utils_add(path, L".txt"); + file_stream s; + s.open(path_bin, L"wb"); + if (s.check_not_null()) + hnd_itm_write_inner(this, s); + free_def(path_bin); +} + +void hnd_itm::write(void** data, size_t* size) { + if (!data || !size || !ready) + return; + + memory_stream s; + s.open(); + hnd_itm_write_inner(this, s); + s.copy(data, size); +} + +bool hnd_itm::load_file(void* data, const char* path, const char* file, uint32_t hash) { + size_t file_len = utf8_length(file); + + const char* t = strrchr(file, '.'); + if (t) + file_len = t - file; + + std::string s; + s.assign(path); + s.append(file, file_len); + + hnd_itm* itm_tbl = (hnd_itm*)data; + itm_tbl->read(s.c_str()); + + return itm_tbl->ready; +} + +static void hnd_itm_read_inner(hnd_itm* itm_tbl, stream& s) { + void* itm_tbl_data = force_malloc(s.length); + s.read(itm_tbl_data, s.length); + hnd_itm_read_text(itm_tbl, itm_tbl_data, s.length); + free_def(itm_tbl_data); + + itm_tbl->ready = true; +} + +static void hnd_itm_write_inner(hnd_itm* itm_tbl, stream& s) { + void* itm_tbl_data = 0; + size_t itm_tbl_data_length = 0; + hnd_itm_write_text(itm_tbl, &itm_tbl_data, &itm_tbl_data_length); + s.write(itm_tbl_data, itm_tbl_data_length); + free_def(itm_tbl_data); +} + +static void hnd_itm_read_text(hnd_itm* itm_tbl, void* data, size_t size) { + key_val kv; + kv.parse(data, size); + + itm_tbl->data.clear(); + int32_t count; + if (kv.read("data", "length", count)) { + std::vector& vd = itm_tbl->data; + + vd.resize(count); + for (int32_t i = 0; i < count; i++) { + if (!kv.open_scope_fmt(i)) + continue; + + hnd_itm_data* d = &vd[i]; + + kv.read("objname_left", d->objname_left); + kv.read("objname_right", d->objname_right); + kv.read("item_str", d->item_str); + kv.read("item_name", d->item_name); + kv.read("file_size", d->file_size); + kv.read("hand_motion", d->hand_motion); + kv.read("hand_scale", d->hand_scale); + kv.read("uid", d->uid); + + kv.close_scope(); + } + kv.close_scope(); + } +} + +static void hnd_itm_write_text(hnd_itm* itm_tbl, void** data, size_t* size) { + memory_stream s; + s.open(); + + key_val_out kv; + if (itm_tbl->data.size() > 0) { + kv.open_scope("cos"); + + int32_t count = (int32_t)itm_tbl->data.size(); + hnd_itm_data* vd = itm_tbl->data.data(); + + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); + for (int32_t i = 0; i < count; i++) { + kv.open_scope_fmt(sort_index_data[i]); + hnd_itm_data* d = &vd[sort_index_data[i]]; + + kv.write(s, "objname_left", d->objname_left); + kv.write(s, "objname_right", d->objname_right); + kv.write(s, "item_str", d->item_str); + kv.write(s, "item_name", d->item_name); + kv.write(s, "file_size", d->file_size); + kv.write(s, "hand_motion", d->hand_motion); + kv.write(s, "hand_scale", d->hand_scale); + kv.write(s, "uid", d->uid); + + kv.close_scope(); + } + + kv.write(s, "length", count); + kv.close_scope(); + } + + s.copy(data, size); +} \ No newline at end of file diff --git a/src/KKdLib/database/hand_item.hpp b/src/KKdLib/database/hand_item.hpp new file mode 100644 index 00000000..a191b479 --- /dev/null +++ b/src/KKdLib/database/hand_item.hpp @@ -0,0 +1,46 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#pragma once + +#include +#include +#include +#include +#include "../default.hpp" +#include "../image.hpp" +#include "../vec.hpp" + +struct hnd_itm_data { + std::string objname_left; + std::string objname_right; + std::string item_str; + std::string item_name; + uint32_t file_size; + std::string hand_motion; + float_t hand_scale; + int32_t uid; + + hnd_itm_data(); + ~hnd_itm_data(); +}; + +struct hnd_itm { + bool ready; + + std::vector data; + + hnd_itm(); + ~hnd_itm(); + + void read(const char* path); + void read(const wchar_t* path); + void read(const void* data, size_t size); + void write(const char* path); + void write(const wchar_t* path); + void write(void** data, size_t* size); + + static bool load_file(void* data, const char* path, const char* file, uint32_t hash); +}; diff --git a/src/KKdLib/database/item_table.cpp b/src/KKdLib/database/item_table.cpp index 9e08d5b2..31290578 100644 --- a/src/KKdLib/database/item_table.cpp +++ b/src/KKdLib/database/item_table.cpp @@ -72,6 +72,67 @@ static void itm_table_write_inner(itm_table* itm_tbl, stream& s); static void itm_table_read_text(itm_table* itm_tbl, void* data, size_t size); static void itm_table_write_text(itm_table* itm_tbl, void** data, size_t* size); +itm_table_item_data_obj::itm_table_item_data_obj() : rpk() { + +} + +itm_table_item_data_obj::~itm_table_item_data_obj() { + +} + +itm_table_item_data_ofs::itm_table_item_data_ofs() : sub_id(), no() { + +} + +itm_table_item_data_tex::itm_table_item_data_tex() { + +} + +itm_table_item_data_tex::~itm_table_item_data_tex() { + +} + +itm_table_item_data_col::itm_table_item_data_col() : flag(), col_tone() { + +} + +itm_table_item_data_col::~itm_table_item_data_col() { + +} + +itm_table_item_data::itm_table_item_data() { + +} + +itm_table_item_data::~itm_table_item_data() { + +} + +itm_table_item::itm_table_item() : no(), flag(), type(), attr(), des_id(), +sub_id(), exclusion(), point(), org_itm(), npr_flag(), face_depth() { + +} + +itm_table_item::~itm_table_item() { + +} + +itm_table_cos::itm_table_cos() : id() { + +} + +itm_table_cos::~itm_table_cos() { + +} + +itm_table_dbgset::itm_table_dbgset() { + +} + +itm_table_dbgset::~itm_table_dbgset() { + +} + itm_table::itm_table() : ready() { } @@ -202,67 +263,6 @@ const char* chara_index_get_name(chara_index chara_index) { return 0; } -itm_table_item_data_ofs::itm_table_item_data_ofs() : sub_id(), no() { - -} - -itm_table_item_data_tex::itm_table_item_data_tex() { - -} - -itm_table_item_data_tex::~itm_table_item_data_tex() { - -} - -itm_table_item_data_obj::itm_table_item_data_obj() : rpk() { - -} - -itm_table_item_data_obj::~itm_table_item_data_obj() { - -} - -itm_table_item_data_col::itm_table_item_data_col() : flag(), col_tone() { - -} - -itm_table_item_data_col::~itm_table_item_data_col() { - -} - -itm_table_item_data::itm_table_item_data() { - -} - -itm_table_item_data::~itm_table_item_data() { - -} - -itm_table_item::itm_table_item() : no(), flag(), type(), attr(), des_id(), -sub_id(), exclusion(), point(), org_itm(), npr_flag(), face_depth() { - -} - -itm_table_item::~itm_table_item() { - -} - -itm_table_cos::itm_table_cos() : id() { - -} - -itm_table_cos::~itm_table_cos() { - -} - -itm_table_dbgset::itm_table_dbgset() { - -} - -itm_table_dbgset::~itm_table_dbgset() { - -} - static void itm_table_read_inner(itm_table* itm_tbl, stream& s) { void* itm_tbl_data = force_malloc(s.length); s.read(itm_tbl_data, s.length); @@ -289,18 +289,17 @@ static void itm_table_read_text(itm_table* itm_tbl, void* data, size_t size) { if (kv.read("cos", "length", count)) { std::vector& vc = itm_tbl->cos; - vc.resize(count); + vc.reserve(count); for (int32_t i = 0; i < count; i++) { if (!kv.open_scope_fmt(i)) continue; - itm_table_cos* c = &vc[i]; - - kv.read("id", c->id); + itm_table_cos c; + kv.read("id", c.id); int32_t count; if (kv.read("item", "length", count)) { - std::vector& vci = c->item; + std::vector& vci = c.item; vci.resize(count); for (int32_t j = 0; j < count; j++) { @@ -312,6 +311,9 @@ static void itm_table_read_text(itm_table* itm_tbl, void* data, size_t size) { } kv.close_scope(); } + + vc.push_back(c); + kv.close_scope(); } kv.close_scope(); @@ -321,16 +323,15 @@ static void itm_table_read_text(itm_table* itm_tbl, void* data, size_t size) { if (kv.read("dbgset", "length", count)) { std::vector& vd = itm_tbl->dbgset; - vd.resize(count); + vd.reserve(count); for (int32_t i = 0; i < count; i++) { if (!kv.open_scope_fmt(i)) continue; - itm_table_dbgset* d = &vd[i]; - + itm_table_dbgset d; int32_t count; if (kv.read("item", "length", count)) { - std::vector& vdi = d->item; + std::vector& vdi = d.item; vdi.resize(count); for (int32_t j = 0; j < count; j++) { @@ -343,7 +344,9 @@ static void itm_table_read_text(itm_table* itm_tbl, void* data, size_t size) { kv.close_scope(); } - kv.read("name", d->name); + kv.read("name", d.name); + vd.push_back(d); + kv.close_scope(); } kv.close_scope(); @@ -528,25 +531,29 @@ static void itm_table_write_text(itm_table* itm_tbl, void** data, size_t* size) if (itm_tbl->cos.size() > 0) { kv.open_scope("cos"); - std::vector& vc = itm_tbl->cos; - int32_t count = (int32_t)vc.size(); + int32_t count = (int32_t)itm_tbl->cos.size(); + itm_table_cos* vc = itm_tbl->cos.data(); + std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); for (int32_t i = 0; i < count; i++) { - kv.open_scope_fmt(sort_index[i]); - itm_table_cos* c = &vc[sort_index[i]]; + kv.open_scope_fmt(sort_index_data[i]); + itm_table_cos* c = &vc[sort_index_data[i]]; kv.write(s, "id", c->id); kv.open_scope("item"); - std::vector& vci = c->item; - int32_t count = (int32_t)vci.size(); - std::vector sort_index1; - key_val_out::get_lexicographic_order(&sort_index1, count); + int32_t count = (int32_t)c->item.size(); + int32_t* vci = c->item.data(); + + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); for (int32_t j = 0; j < count; j++) { - kv.open_scope_fmt(sort_index1[j]); - kv.write(s, vci[sort_index1[j]]); + kv.open_scope_fmt(sort_index_data[j]); + kv.write(s, vci[sort_index_data[j]]); kv.close_scope(); } @@ -563,24 +570,28 @@ static void itm_table_write_text(itm_table* itm_tbl, void** data, size_t* size) if (itm_tbl->dbgset.size() > 0) { kv.open_scope("dbgset"); - std::vector& vd = itm_tbl->dbgset; - int32_t count = (int32_t)vd.size(); - std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); - for (int32_t i = 0; i < count; i++) { - kv.open_scope_fmt(sort_index[i]); + int32_t count = (int32_t)itm_tbl->dbgset.size(); + itm_table_dbgset* vd = itm_tbl->dbgset.data(); - itm_table_dbgset* d = &vd[sort_index[i]]; + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); + for (int32_t i = 0; i < count; i++) { + kv.open_scope_fmt(sort_index_data[i]); + + itm_table_dbgset* d = &vd[sort_index_data[i]]; kv.open_scope("item"); - std::vector& vdi = d->item; - int32_t count = (int32_t)vdi.size(); - std::vector sort_index1; - key_val_out::get_lexicographic_order(&sort_index1, count); + int32_t count = (int32_t)d->item.size(); + int32_t* vdi = d->item.data(); + + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); for (int32_t j = 0; j < count; j++) { - kv.open_scope_fmt(sort_index1[j]); - kv.write(s, vdi[sort_index1[j]]); + kv.open_scope_fmt(sort_index_data[j]); + kv.write(s, vdi[sort_index_data[j]]); kv.close_scope(); } @@ -598,14 +609,16 @@ static void itm_table_write_text(itm_table* itm_tbl, void** data, size_t* size) if (itm_tbl->item.size() > 0) { kv.open_scope("item"); - std::vector& vi = itm_tbl->item; - int32_t count = (int32_t)vi.size(); - std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); - for (int32_t i = 0; i < count; i++) { - kv.open_scope_fmt(sort_index[i]); + int32_t count = (int32_t)itm_tbl->item.size(); + itm_table_item* vi = itm_tbl->item.data(); - itm_table_item* itm = &vi[sort_index[i]]; + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); + for (int32_t i = 0; i < count; i++) { + kv.open_scope_fmt(sort_index_data[i]); + + itm_table_item* itm = &vi[sort_index_data[i]]; kv.write(s, "attr", itm->attr); if (itm->data.col.size() || itm->data.obj.size() @@ -615,14 +628,16 @@ static void itm_table_write_text(itm_table* itm_tbl, void** data, size_t* size) if (itm->data.col.size()) { kv.open_scope("col"); - std::vector& vidc = itm->data.col; - int32_t count = (int32_t)vidc.size(); - std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); - for (int32_t j = 0; j < count; j++) { - kv.open_scope_fmt(sort_index[j]); + int32_t count = (int32_t)itm->data.col.size(); + itm_table_item_data_col* vidc = itm-> data.col.data(); - itm_table_item_data_col* col = &vidc[sort_index[j]]; + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); + for (int32_t j = 0; j < count; j++) { + kv.open_scope_fmt(sort_index_data[j]); + + itm_table_item_data_col* col = &vidc[sort_index_data[j]]; if (col->flag & 0x01) { kv.write(s, "blend.0", col->col_tone.blend.x); kv.write(s, "blend.1", col->col_tone.blend.y); @@ -656,14 +671,16 @@ static void itm_table_write_text(itm_table* itm_tbl, void** data, size_t* size) if (itm->data.obj.size()) { kv.open_scope("obj"); - std::vector& vido = itm->data.obj; - int32_t count = (int32_t)vido.size(); - std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); - for (int32_t j = 0; j < count; j++) { - kv.open_scope_fmt(sort_index[j]); + int32_t count = (int32_t)itm->data.obj.size(); + itm_table_item_data_obj* vido = itm->data.obj.data(); - itm_table_item_data_obj* obj = &vido[sort_index[j]]; + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); + for (int32_t j = 0; j < count; j++) { + kv.open_scope_fmt(sort_index_data[j]); + + itm_table_item_data_obj* obj = &vido[sort_index_data[j]]; kv.write(s, "rpk", obj->rpk); kv.write(s, "uid", obj->uid); @@ -677,14 +694,16 @@ static void itm_table_write_text(itm_table* itm_tbl, void** data, size_t* size) if (itm->data.ofs.size()) { kv.open_scope("ofs"); - std::vector& vidof = itm->data.ofs; - int32_t count = (int32_t)vidof.size(); - std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); - for (int32_t j = 0; j < count; j++) { - kv.open_scope_fmt(sort_index[j]); + int32_t count = (int32_t)itm->data.ofs.size(); + itm_table_item_data_ofs* vido = itm->data.ofs.data(); - itm_table_item_data_ofs* ofs = &vidof[sort_index[j]]; + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); + for (int32_t j = 0; j < count; j++) { + kv.open_scope_fmt(sort_index_data[j]); + + itm_table_item_data_ofs* ofs = &vido[sort_index_data[j]]; kv.write(s, "no", ofs->no); kv.write(s, "rx", ofs->rotation.x); kv.write(s, "ry", ofs->rotation.y); @@ -707,14 +726,16 @@ static void itm_table_write_text(itm_table* itm_tbl, void** data, size_t* size) if (itm->data.tex.size()) { kv.open_scope("tex"); - std::vector& vidt = itm->data.tex; - int32_t count = (int32_t)vidt.size(); - std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); - for (int32_t j = 0; j < count; j++) { - kv.open_scope_fmt(sort_index[j]); + int32_t count = (int32_t)itm->data.tex.size(); + itm_table_item_data_tex* vidt = itm->data.tex.data(); - itm_table_item_data_tex* tex = &vidt[sort_index[j]]; + std::vector sort_index; + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); + for (int32_t j = 0; j < count; j++) { + kv.open_scope_fmt(sort_index_data[j]); + + itm_table_item_data_tex* tex = &vidt[sort_index_data[j]]; kv.write(s, "chg", tex->chg); kv.write(s, "org", tex->org); @@ -739,13 +760,15 @@ static void itm_table_write_text(itm_table* itm_tbl, void** data, size_t* size) if (itm->objset.size()) { kv.open_scope("objset"); - std::vector& vio = itm->objset; - int32_t count = (int32_t)vio.size(); + int32_t count = (int32_t)itm->objset.size(); + std::string* vio = itm->objset.data(); + std::vector sort_index; - key_val_out::get_lexicographic_order(&sort_index, count); + key_val_out::get_lexicographic_order(sort_index, count); + int32_t* sort_index_data = sort_index.data(); for (int32_t j = 0; j < count; j++) { - kv.open_scope_fmt(sort_index[j]); - kv.write(s, vio[sort_index[j]]); + kv.open_scope_fmt(sort_index_data[j]); + kv.write(s, vio[sort_index_data[j]]); kv.close_scope(); } diff --git a/src/KKdLib/database/motion.cpp b/src/KKdLib/database/motion.cpp index b45d04c7..08489011 100644 --- a/src/KKdLib/database/motion.cpp +++ b/src/KKdLib/database/motion.cpp @@ -14,6 +14,40 @@ static void motion_database_file_read_inner(motion_database_file* mot_db, stream& s); static void motion_database_file_write_inner(motion_database_file* mot_db, stream& s); +motion_info_file::motion_info_file() { + id = (uint32_t)-1; +} + +motion_info_file::~motion_info_file() { + +} + +motion_set_info_file::motion_set_info_file() { + id = (uint32_t)-1; +} + +motion_set_info_file::~motion_set_info_file() { + +} + +motion_info::motion_info() { + name_hash = hash_murmurhash_empty; + id = (uint32_t)-1; +} + +motion_info::~motion_info() { + +} + +motion_set_info::motion_set_info() { + name_hash = hash_murmurhash_empty; + id = (uint32_t)-1; +} + +motion_set_info::~motion_set_info() { + +} + motion_database_file::motion_database_file() : ready() { } @@ -177,180 +211,146 @@ void motion_database::add(motion_database_file* mot_db_file) { } } -motion_set_info* motion_database::get_motion_set_by_id(uint32_t id) { +const motion_set_info* motion_database::get_motion_set_by_id(uint32_t id) const { if (id == -1) return 0; - for (motion_set_info& i : motion_set) + for (const motion_set_info& i : motion_set) if (id == i.id) return &i; return 0; } -motion_set_info* motion_database::get_motion_set_by_name(const char* name) { +const motion_set_info* motion_database::get_motion_set_by_name(const char* name) const { if (!name) return 0; uint32_t name_hash = hash_utf8_murmurhash(name); - for (motion_set_info& i : motion_set) + for (const motion_set_info& i : motion_set) if (name_hash == i.name_hash) return &i; return 0; } -motion_set_info* motion_database::get_motion_set_by_motion_id(uint32_t id) { +const motion_set_info* motion_database::get_motion_set_by_motion_id(uint32_t id) const { if (id == -1) return 0; - for (motion_set_info& i : motion_set) - for (motion_info& j : i.motion) + for (const motion_set_info& i : motion_set) + for (const motion_info& j : i.motion) if (id == j.id) return &i; return 0; } -motion_set_info* motion_database::get_motion_set_by_motion_name(const char* name) { +const motion_set_info* motion_database::get_motion_set_by_motion_name(const char* name) const { if (!name) return 0; uint32_t name_hash = hash_utf8_murmurhash(name); - for (motion_set_info& i : motion_set) - for (motion_info& j : i.motion) + for (const motion_set_info& i : motion_set) + for (const motion_info& j : i.motion) if (name_hash == j.name_hash) return &i; return 0; } -uint32_t motion_database::get_motion_set_id_by_motion_id(uint32_t id) { +uint32_t motion_database::get_motion_set_id_by_motion_id(uint32_t id) const { if (id == -1) return -1; - for (motion_set_info& i : motion_set) - for (motion_info& j : i.motion) + for (const motion_set_info& i : motion_set) + for (const motion_info& j : i.motion) if (id == j.id) return i.id; return -1; } -uint32_t motion_database::get_motion_set_id_by_motion_name(const char* name) { +uint32_t motion_database::get_motion_set_id_by_motion_name(const char* name) const { if (!name) return -1; uint32_t name_hash = hash_utf8_murmurhash(name); - for (motion_set_info& i : motion_set) - for (motion_info& j : i.motion) + for (const motion_set_info& i : motion_set) + for (const motion_info& j : i.motion) if (name_hash == j.name_hash) return i.id; return -1; } -uint32_t motion_database::get_motion_set_id(const char* name) { +uint32_t motion_database::get_motion_set_id(const char* name) const { if (!name) return -1; uint32_t name_hash = hash_utf8_murmurhash(name); - for (motion_set_info& i : motion_set) + for (const motion_set_info& i : motion_set) if (name_hash == i.name_hash) return i.id; return -1; } -const char* motion_database::get_motion_set_name(uint32_t id) { +const char* motion_database::get_motion_set_name(uint32_t id) const { if (id == -1) return 0; - for (motion_set_info& i : motion_set) + for (const motion_set_info& i : motion_set) if (id == i.id) return i.name.c_str(); return 0; } -motion_info* motion_database::get_motion_by_id(uint32_t id) { +const motion_info* motion_database::get_motion_by_id(uint32_t id) const { if (id == -1) return 0; - for (motion_set_info& i : motion_set) - for (motion_info& j : i.motion) + for (const motion_set_info& i : motion_set) + for (const motion_info& j : i.motion) if (id == j.id) return &j; return 0; } -motion_info* motion_database::get_motion_by_name(const char* name) { +const motion_info* motion_database::get_motion_by_name(const char* name) const { if (!name) return 0; uint32_t name_hash = hash_utf8_murmurhash(name); - for (motion_set_info& i : motion_set) - for (motion_info& j : i.motion) + for (const motion_set_info& i : motion_set) + for (const motion_info& j : i.motion) if (name_hash == j.name_hash) return &j; return 0; } -uint32_t motion_database::get_motion_id(const char* name) { +uint32_t motion_database::get_motion_id(const char* name) const { if (!name) return -1; uint32_t name_hash = hash_utf8_murmurhash(name); - for (motion_set_info& i : motion_set) - for (motion_info& j : i.motion) + for (const motion_set_info& i : motion_set) + for (const motion_info& j : i.motion) if (name_hash == j.name_hash) return j.id; return -1; } -const char* motion_database::get_motion_name(uint32_t id) { +const char* motion_database::get_motion_name(uint32_t id) const { if (id == -1) return 0; - for (motion_set_info& i : motion_set) - for (motion_info& j : i.motion) + for (const motion_set_info& i : motion_set) + for (const motion_info& j : i.motion) if (id == j.id) return j.name.c_str(); return 0; } -motion_info_file::motion_info_file() { - id = (uint32_t)-1; -} - -motion_info_file::~motion_info_file() { - -} - -motion_set_info_file::motion_set_info_file() { - id = (uint32_t)-1; -} - -motion_set_info_file::~motion_set_info_file() { - -} - -motion_info::motion_info() { - name_hash = hash_murmurhash_empty; - id = (uint32_t)-1; -} - -motion_info::~motion_info() { - -} - -motion_set_info::motion_set_info() { - name_hash = hash_murmurhash_empty; - id = (uint32_t)-1; -} - -motion_set_info::~motion_set_info() { - -} - static void motion_database_file_read_inner(motion_database_file* mot_db, stream& s) { if (s.read_uint32_t() != 0x01) return; diff --git a/src/KKdLib/database/motion.hpp b/src/KKdLib/database/motion.hpp index 89b791f1..607b6086 100644 --- a/src/KKdLib/database/motion.hpp +++ b/src/KKdLib/database/motion.hpp @@ -73,16 +73,16 @@ struct motion_database { void add(motion_database_file* mot_db_file); - motion_set_info* get_motion_set_by_id(uint32_t id); - motion_set_info* get_motion_set_by_name(const char* name); - motion_set_info* get_motion_set_by_motion_id(uint32_t id); - motion_set_info* get_motion_set_by_motion_name(const char* name); - uint32_t get_motion_set_id_by_motion_id(uint32_t id); - uint32_t get_motion_set_id_by_motion_name(const char* name); - uint32_t get_motion_set_id(const char* name); - const char* get_motion_set_name(uint32_t id); - motion_info* get_motion_by_id(uint32_t id); - motion_info* get_motion_by_name(const char* name); - uint32_t get_motion_id(const char* name); - const char* get_motion_name(uint32_t id); + const motion_set_info* get_motion_set_by_id(uint32_t id) const; + const motion_set_info* get_motion_set_by_name(const char* name) const; + const motion_set_info* get_motion_set_by_motion_id(uint32_t id) const; + const motion_set_info* get_motion_set_by_motion_name(const char* name) const; + uint32_t get_motion_set_id_by_motion_id(uint32_t id) const; + uint32_t get_motion_set_id_by_motion_name(const char* name) const; + uint32_t get_motion_set_id(const char* name) const; + const char* get_motion_set_name(uint32_t id) const; + const motion_info* get_motion_by_id(uint32_t id) const; + const motion_info* get_motion_by_name(const char* name) const; + uint32_t get_motion_id(const char* name) const; + const char* get_motion_name(uint32_t id) const; }; diff --git a/src/KKdLib/database/object.cpp b/src/KKdLib/database/object.cpp index 2f939c38..18852b2a 100644 --- a/src/KKdLib/database/object.cpp +++ b/src/KKdLib/database/object.cpp @@ -20,16 +20,6 @@ static int64_t object_database_strings_get_string_offset(std::vector& vec_off, std::string& str); static void object_database_strings_push_back_check(std::vector& vec, std::string& str); -object_info::object_info() { - this->id = (uint32_t)-1; - this->set_id = (uint32_t)-1; -} - -object_info::object_info(uint32_t id, uint32_t set_id) { - this->id = id; - this->set_id = set_id; -} - object_set_info_file::object_set_info_file() : id() { } @@ -290,7 +280,7 @@ void object_database::add(object_database_file* obj_db_file) { } } -bool object_database::get_object_set_info(const char* name, object_set_info** set_info) { +bool object_database::get_object_set_info(const char* name, const object_set_info** set_info) const { if (!set_info) return false; *set_info = 0; @@ -300,7 +290,7 @@ bool object_database::get_object_set_info(const char* name, object_set_info** se uint32_t name_hash = hash_utf8_murmurhash(name); - for (object_set_info& i : object_set) + for (const object_set_info& i : object_set) if (name_hash == i.name_hash) { *set_info = &i; return true; @@ -308,7 +298,7 @@ bool object_database::get_object_set_info(const char* name, object_set_info** se return false; } -bool object_database::get_object_set_info(uint32_t set_id, object_set_info** set_info) { +bool object_database::get_object_set_info(uint32_t set_id, const object_set_info** set_info) const { if (!set_info) return false; *set_info = 0; @@ -316,7 +306,7 @@ bool object_database::get_object_set_info(uint32_t set_id, object_set_info** set if (set_id == -1) return false; - for (object_set_info& i : object_set) + for (const object_set_info& i : object_set) if (set_id == i.id) { *set_info = &i; return true; @@ -324,7 +314,7 @@ bool object_database::get_object_set_info(uint32_t set_id, object_set_info** set return false; } -bool object_database::get_object_info_data(const char* name, object_info_data** info) { +bool object_database::get_object_info_data(const char* name, const object_info_data** info) const { if (!info) return false; *info = 0; @@ -334,8 +324,8 @@ bool object_database::get_object_info_data(const char* name, object_info_data** uint32_t name_hash = hash_utf8_murmurhash(name); - for (object_set_info& i : object_set) - for (object_info_data& j : i.object) + for (const object_set_info& i : object_set) + for (const object_info_data& j : i.object) if (name_hash == j.name_hash_murmurhash) { *info = &j; return true; @@ -343,7 +333,8 @@ bool object_database::get_object_info_data(const char* name, object_info_data** return false; } -bool object_database::get_object_info_data_by_fnv1a64m_hash(uint64_t hash, object_info_data** info) { +bool object_database::get_object_info_data_by_fnv1a64m_hash( + uint64_t hash, const object_info_data** info) const { if (!info) return false; *info = 0; @@ -351,8 +342,8 @@ bool object_database::get_object_info_data_by_fnv1a64m_hash(uint64_t hash, objec if (hash == hash_fnv1a64m_empty) return false; - for (object_set_info& i : object_set) - for (object_info_data& j : i.object) + for (const object_set_info& i : object_set) + for (const object_info_data& j : i.object) if (hash == j.name_hash_fnv1a64m) { *info = &j; return true; @@ -360,7 +351,8 @@ bool object_database::get_object_info_data_by_fnv1a64m_hash(uint64_t hash, objec return false; } -bool object_database::get_object_info_data_by_fnv1a64m_hash_upper(uint64_t hash, object_info_data** info) { +bool object_database::get_object_info_data_by_fnv1a64m_hash_upper( + uint64_t hash, const object_info_data** info) const { if (!info) return false; *info = 0; @@ -368,8 +360,8 @@ bool object_database::get_object_info_data_by_fnv1a64m_hash_upper(uint64_t hash, if (hash == hash_fnv1a64m_empty) return false; - for (object_set_info& i : object_set) - for (object_info_data& j : i.object) + for (const object_set_info& i : object_set) + for (const object_info_data& j : i.object) if (hash == j.name_hash_fnv1a64m_upper) { *info = &j; return true; @@ -377,7 +369,8 @@ bool object_database::get_object_info_data_by_fnv1a64m_hash_upper(uint64_t hash, return false; } -bool object_database::get_object_info_data_by_murmurhash(uint32_t hash, object_info_data** info) { +bool object_database::get_object_info_data_by_murmurhash( + uint32_t hash, const object_info_data** info) const { if (!info) return false; *info = 0; @@ -385,8 +378,8 @@ bool object_database::get_object_info_data_by_murmurhash(uint32_t hash, object_i if (hash == hash_murmurhash_empty) return false; - for (object_set_info& i : object_set) - for (object_info_data& j : i.object) + for (const object_set_info& i : object_set) + for (const object_info_data& j : i.object) if (hash == j.name_hash_murmurhash) { *info = &j; return true; @@ -394,7 +387,7 @@ bool object_database::get_object_info_data_by_murmurhash(uint32_t hash, object_i return false; } -uint32_t object_database::get_object_set_id(const char* name) { +uint32_t object_database::get_object_set_id(const char* name) const { if (!name) return (uint32_t)-1; @@ -406,25 +399,25 @@ uint32_t object_database::get_object_set_id(const char* name) { if (name_hash == hash_murmurhash_empty) return (uint32_t)-1; - for (object_set_info& i : object_set) + for (const object_set_info& i : object_set) if (name_hash == i.name_hash) return i.id; return (uint32_t)-1; } -const char* object_database::get_object_set_name(uint32_t set_id) { +const char* object_database::get_object_set_name(uint32_t set_id) const { if (set_id == -1) return 0; - for (object_set_info& i : object_set) + for (const object_set_info& i : object_set) if (set_id == i.id) return i.name.c_str(); return 0; } -object_info object_database::get_object_info(const char* name) { +object_info object_database::get_object_info(const char* name) const { if (!name) return object_info(); @@ -436,21 +429,21 @@ object_info object_database::get_object_info(const char* name) { if (name_hash == hash_murmurhash_empty) return object_info(); - for (object_set_info& i : object_set) - for (object_info_data& j : i.object) + for (const object_set_info& i : object_set) + for (const object_info_data& j : i.object) if (name_hash == j.name_hash_murmurhash) return { j.id, i.id }; return object_info(); } -const char* object_database::get_object_name(object_info obj_info) { +const char* object_database::get_object_name(object_info obj_info) const { if (obj_info.is_null()) return 0; - for (object_set_info& i : object_set) + for (const object_set_info& i : object_set) if (obj_info.set_id == i.id) - for (object_info_data& j : i.object) + for (const object_info_data& j : i.object) if (obj_info.id == j.id) return j.name.c_str(); diff --git a/src/KKdLib/database/object.hpp b/src/KKdLib/database/object.hpp index 106a658d..8734034f 100644 --- a/src/KKdLib/database/object.hpp +++ b/src/KKdLib/database/object.hpp @@ -14,34 +14,37 @@ struct object_info { uint32_t id; uint32_t set_id; - object_info(); - object_info(uint32_t id, uint32_t set_id); - bool is_null(); - bool not_null(); - bool is_null_modern(); - bool not_null_modern(); + inline object_info() { + this->id = (uint32_t)-1; + this->set_id = (uint32_t)-1; + } + + inline object_info(uint32_t id, uint32_t set_id) { + this->id = id; + this->set_id = set_id; + } + + inline bool is_null() const { + return id == (uint32_t)-1 && set_id == (uint32_t)-1; + } + + inline bool not_null() const { + return id != (uint32_t)-1 || set_id != (uint32_t)-1; + } + + inline bool is_null_modern() const { + return id == (uint32_t)-1 && set_id == (uint32_t)-1 + || id == hash_murmurhash_empty || id == hash_murmurhash_null + || set_id == hash_murmurhash_empty || set_id == hash_murmurhash_null; + } + + inline bool not_null_modern() const { + return id != (uint32_t)-1 || set_id != (uint32_t)-1 + || (id != hash_murmurhash_empty && id != hash_murmurhash_null + && set_id != hash_murmurhash_empty && set_id != hash_murmurhash_null); + } }; -inline bool object_info::is_null() { - return id == (uint32_t)-1 && set_id == (uint32_t)-1; -} - -inline bool object_info::not_null() { - return id != (uint32_t)-1 || set_id != (uint32_t)-1; -} - -inline bool object_info::is_null_modern() { - return id == (uint32_t)-1 && set_id == (uint32_t)-1 - || id == hash_murmurhash_empty || id == hash_murmurhash_null - || set_id == hash_murmurhash_empty || set_id == hash_murmurhash_null; -} - -inline bool object_info::not_null_modern() { - return id != (uint32_t)-1 || set_id != (uint32_t)-1 - || (id != hash_murmurhash_empty && id != hash_murmurhash_null - && set_id != hash_murmurhash_empty && set_id != hash_murmurhash_null); -} - inline bool operator >(const object_info& left, const object_info& right) { return left.set_id > right.set_id && left.id > right.id; } @@ -139,14 +142,17 @@ struct object_database { void add(object_database_file* obj_db_file); - bool get_object_set_info(const char* name, object_set_info** set_info); - bool get_object_set_info(uint32_t set_id, object_set_info** set_info); - bool get_object_info_data(const char* name, object_info_data** info); - bool get_object_info_data_by_fnv1a64m_hash(uint64_t hash, object_info_data** info); - bool get_object_info_data_by_fnv1a64m_hash_upper(uint64_t hash, object_info_data** info); - bool get_object_info_data_by_murmurhash(uint32_t hash, object_info_data** info); - uint32_t get_object_set_id(const char* name); - const char* get_object_set_name(uint32_t set_id); - object_info get_object_info(const char* name); - const char* get_object_name(object_info obj_info); + bool get_object_set_info(const char* name, const object_set_info** set_info) const; + bool get_object_set_info(uint32_t set_id, const object_set_info** set_info) const; + bool get_object_info_data(const char* name, const object_info_data** info) const; + bool get_object_info_data_by_fnv1a64m_hash( + uint64_t hash, const object_info_data** info) const; + bool get_object_info_data_by_fnv1a64m_hash_upper( + uint64_t hash, const object_info_data** info) const; + bool get_object_info_data_by_murmurhash( + uint32_t hash, const object_info_data** info) const; + uint32_t get_object_set_id(const char* name) const; + const char* get_object_set_name(uint32_t set_id) const; + object_info get_object_info(const char* name) const; + const char* get_object_name(object_info obj_info) const; }; diff --git a/src/KKdLib/database/stage.cpp b/src/KKdLib/database/stage.cpp index d1d7de58..212ba746 100644 --- a/src/KKdLib/database/stage.cpp +++ b/src/KKdLib/database/stage.cpp @@ -388,35 +388,35 @@ void stage_database::add(stage_database_file* stage_data_file) { } } -::stage_data* stage_database::get_stage_data(int32_t stage_index) { +const ::stage_data* stage_database::get_stage_data(int32_t stage_index) const { if (stage_index >= 0 && stage_index < stage_data.size()) return &stage_data[stage_index]; return 0; } -::stage_data_modern* stage_database::get_stage_data_modern(uint32_t stage_hash) { - for (::stage_data_modern& i : stage_modern) +const ::stage_data_modern* stage_database::get_stage_data_modern(uint32_t stage_hash) const { + for (const ::stage_data_modern& i : stage_modern) if (i.hash == stage_hash) return &i; return 0; } -int32_t stage_database::get_stage_index(const char* name) { +int32_t stage_database::get_stage_index(const char* name) const { uint32_t name_hash = hash_utf8_murmurhash(name); - for (::stage_data& i : stage_data) + for (const ::stage_data& i : stage_data) if (name_hash == i.name_hash) return (int32_t)(&i - stage_data.data()); return -1; } -const char* stage_database::get_stage_name(int32_t stage_index) { +const char* stage_database::get_stage_name(int32_t stage_index) const { if (stage_index >= 0 && stage_index < stage_data.size()) return stage_data[stage_index].name.c_str(); return 0; } -const char* stage_database::get_stage_name_modern(uint32_t stage_hash) { - for (::stage_data_modern& i : stage_modern) +const char* stage_database::get_stage_name_modern(uint32_t stage_hash) const { + for (const ::stage_data_modern& i : stage_modern) if (i.hash == stage_hash) return i.name.c_str(); return 0; diff --git a/src/KKdLib/database/stage.hpp b/src/KKdLib/database/stage.hpp index 0c691118..d8259d76 100644 --- a/src/KKdLib/database/stage.hpp +++ b/src/KKdLib/database/stage.hpp @@ -273,11 +273,11 @@ struct stage_database { void add(stage_database_file* stage_data_file); - ::stage_data* get_stage_data(int32_t stage_index); - ::stage_data_modern* get_stage_data_modern(uint32_t stage_hash); - int32_t get_stage_index(const char* name); - const char* get_stage_name(int32_t stage_index); - const char* get_stage_name_modern(uint32_t stage_hash); + const ::stage_data* get_stage_data(int32_t stage_index) const; + const ::stage_data_modern* get_stage_data_modern(uint32_t stage_hash) const; + int32_t get_stage_index(const char* name) const; + const char* get_stage_name(int32_t stage_index) const; + const char* get_stage_name_modern(uint32_t stage_hash) const; }; extern const stage_effects stage_effects_default; diff --git a/src/KKdLib/database/texture.cpp b/src/KKdLib/database/texture.cpp index 4a7e3184..311a09ac 100644 --- a/src/KKdLib/database/texture.cpp +++ b/src/KKdLib/database/texture.cpp @@ -227,44 +227,78 @@ void texture_database::add(texture_database_file* tex_db_file) { texture.reserve(tex_db_file->texture.size()); + size_t src_size = texture.size(); + size_t dst_size = src_size; + for (texture_info_file& i : tex_db_file->texture) { uint32_t id = i.id; - texture_info* info = 0; - for (texture_info& j : texture) - if (id == j.id) { - info = &j; - break; - } + texture_info* info = texture.data(); + if (info) { + size_t length = src_size; + size_t temp; + while (length > 0) + if (id <= info[temp = length / 2].id) + length /= 2; + else { + info += temp + 1; + length -= temp + 1; + } + + if (info == texture.data() + src_size || id != info->id) + info = 0; + } + + if (!info) { + texture_info* j_begin = texture.data() + src_size; + texture_info* j_end = texture.data() + dst_size; + for (texture_info* j = j_begin; j != j_end; j++) + if (id == j->id) { + info = j; + break; + } + } if (!info) { texture.push_back({}); info = &texture.back(); + dst_size++; } info->id = i.id; info->name.assign(i.name); info->name_hash = hash_string_murmurhash(info->name); } + + sort(); } -uint32_t texture_database::get_texture_id(const char* name) { +static size_t texture_info_radix_index_func_id(texture_info* data, size_t index) { + return data[index].id; +} + +void texture_database::sort() { + radix_sort_custom(texture.data(), texture.size(), sizeof(texture_info), + sizeof(uint32_t), (radix_index_func)texture_info_radix_index_func_id); +} + +uint32_t texture_database::get_texture_id(const char* name) const { if (!name) return -1; uint32_t name_hash = hash_utf8_murmurhash(name); - for (texture_info& i : texture) + for (const texture_info& i : texture) if (name_hash == i.name_hash) return i.id; return -1; } -const char* texture_database::get_texture_name(uint32_t id) { +const char* texture_database::get_texture_name(uint32_t id) const { if (id == -1) return 0; - for (texture_info& i : texture) + for (const texture_info& i : texture) if (id == i.id) return i.name.c_str(); return 0; diff --git a/src/KKdLib/database/texture.hpp b/src/KKdLib/database/texture.hpp index 6321b507..baf2acd5 100644 --- a/src/KKdLib/database/texture.hpp +++ b/src/KKdLib/database/texture.hpp @@ -54,7 +54,8 @@ struct texture_database { ~texture_database(); void add(texture_database_file* tex_db_file); + void sort(); - uint32_t get_texture_id(const char* name); - const char* get_texture_name(uint32_t id); + uint32_t get_texture_id(const char* name) const; + const char* get_texture_name(uint32_t id) const; }; diff --git a/src/KKdLib/dds.cpp b/src/KKdLib/dds.cpp index 6fb8f9e0..cd7116ce 100644 --- a/src/KKdLib/dds.cpp +++ b/src/KKdLib/dds.cpp @@ -145,7 +145,7 @@ DDSCONST DDS_PIXELFORMAT DDSPF_L8A8 = DDSCONST DDS_PIXELFORMAT DDSPF_A8 = { sizeof(DDS_PIXELFORMAT), DDS_ALPHA, 0, 8, 0, 0, 0, 0xff }; -static void dds_reverse_rgb(txp_format format, int64_t size, uint8_t* data); +static void dds_reverse_rgb(txp_format format, size_t size, uint8_t* data); static bool dds_check_is_dxt1a(int64_t size, uint8_t* data); dds::dds() : format(), width(), height(), mipmaps_count(), has_cube_map() { @@ -235,19 +235,17 @@ void dds::read(const wchar_t* path) { else if (!memcmp(&dds_h.ddspf, &DDSPF_A4B4G4R4, sizeof(DDS_PIXELFORMAT))) format = TXP_RGBA4; else if (!memcmp(&dds_h.ddspf, &DDSPF_DXT1, sizeof(uint32_t) * 3)) - format = TXP_DXT1; + format = TXP_BC1; else if (!memcmp(&dds_h.ddspf, &DDSPF_DXT3, sizeof(uint32_t) * 3)) - format = TXP_DXT3; + format = TXP_BC2; else if (!memcmp(&dds_h.ddspf, &DDSPF_DXT5, sizeof(uint32_t) * 3)) - format = TXP_DXT5; - else if (!memcmp(&dds_h.ddspf, &DDSPF_ATI1, sizeof(uint32_t) * 3)) - format = TXP_ATI1; - else if (!memcmp(&dds_h.ddspf, &DDSPF_BC4_UNORM, sizeof(uint32_t) * 3)) - format = TXP_ATI1; - else if (!memcmp(&dds_h.ddspf, &DDSPF_ATI2, sizeof(uint32_t) * 3)) - format = TXP_ATI2; - else if (!memcmp(&dds_h.ddspf, &DDSPF_BC5_UNORM, sizeof(uint32_t) * 3)) - format = TXP_ATI2; + format = TXP_BC3; + else if (!memcmp(&dds_h.ddspf, &DDSPF_ATI1, sizeof(uint32_t) * 3) + || !memcmp(&dds_h.ddspf, &DDSPF_BC4_UNORM, sizeof(uint32_t) * 3)) + format = TXP_BC4; + else if (!memcmp(&dds_h.ddspf, &DDSPF_ATI2, sizeof(uint32_t) * 3) + || !memcmp(&dds_h.ddspf, &DDSPF_BC5_UNORM, sizeof(uint32_t) * 3)) + format = TXP_BC5; else if (!memcmp(&dds_h.ddspf, &DDSPF_L8, sizeof(DDS_PIXELFORMAT))) format = TXP_L8; else if (!memcmp(&dds_h.ddspf, &DDSPF_L8A8, sizeof(DDS_PIXELFORMAT))) { @@ -275,8 +273,8 @@ void dds::read(const wchar_t* path) { s.read(data, size); if (reverse) dds_reverse_rgb(format, size, (uint8_t*)data); - else if (format == TXP_DXT1 && dds_check_is_dxt1a(size, (uint8_t*)data)) - format = TXP_DXT1a; + else if (format == TXP_BC1 && dds_check_is_dxt1a(size, (uint8_t*)data)) + format = TXP_BC1a; this->data.push_back(data); } while (has_cube_map && data.size() / mipmaps_count < 6); @@ -298,72 +296,74 @@ void dds::write(const wchar_t* path) { if (!this || !path || data.size() < 1) return; + DDS_HEADER dds_h = {}; + dds_h.size = sizeof(DDS_HEADER); + dds_h.flags = DDS_HEADER_FLAGS_TEXTURE | DDSD_LINEARSIZE; + if (mipmaps_count > 1) + dds_h.flags |= DDSD_MIPMAPCOUNT; + dds_h.height = height; + dds_h.width = width; + dds_h.pitchOrLinearSize = txp::get_size(format, width, height); + dds_h.mipMapCount = mipmaps_count; + dds_h.caps |= DDS_SURFACE_FLAGS_TEXTURE; + if (has_cube_map) + dds_h.caps |= DDS_SURFACE_FLAGS_cube_map; + + if (mipmaps_count > 1) + dds_h.caps |= DDS_SURFACE_FLAGS_MIPMAP; + + if (has_cube_map) + dds_h.caps2 |= DDS_CUBE_MAP_ALLFACES; + + switch (format) { + case TXP_A8: + dds_h.ddspf = DDSPF_A8; + break; + case TXP_RGB8: + dds_h.ddspf = DDSPF_R8G8B8; + break; + case TXP_RGBA8: + dds_h.ddspf = DDSPF_A8R8G8B8; + break; + case TXP_RGB5: + dds_h.ddspf = DDSPF_R5G6B5; + break; + case TXP_RGB5A1: + dds_h.ddspf = DDSPF_A1R5G5B5; + break; + case TXP_RGBA4: + dds_h.ddspf = DDSPF_A4R4G4B4; + break; + case TXP_BC1: + case TXP_BC1a: + dds_h.ddspf = DDSPF_DXT1; + break; + case TXP_BC2: + dds_h.ddspf = DDSPF_DXT3; + break; + case TXP_BC3: + dds_h.ddspf = DDSPF_DXT5; + break; + case TXP_BC4: + dds_h.ddspf = DDSPF_ATI1; + break; + case TXP_BC5: + dds_h.ddspf = DDSPF_ATI2; + break; + case TXP_L8: + dds_h.ddspf = DDSPF_L8; + break; + case TXP_L8A8: + dds_h.ddspf = DDSPF_L8A8; + break; + default: + return; + } + wchar_t* path_dds = str_utils_add(path, L".dds"); file_stream s; s.open(path_dds, L"wb"); if (s.check_not_null()) { - DDS_HEADER dds_h = {}; - dds_h.size = sizeof(DDS_HEADER); - dds_h.flags = DDS_HEADER_FLAGS_TEXTURE | DDSD_LINEARSIZE; - if (mipmaps_count > 1) - dds_h.flags |= DDSD_MIPMAPCOUNT; - dds_h.height = height; - dds_h.width = width; - dds_h.pitchOrLinearSize = txp::get_size(format, width, height); - dds_h.mipMapCount = mipmaps_count; - dds_h.caps |= DDS_SURFACE_FLAGS_TEXTURE; - if (has_cube_map) - dds_h.caps |= DDS_SURFACE_FLAGS_cube_map; - - if (mipmaps_count > 1) - dds_h.caps |= DDS_SURFACE_FLAGS_MIPMAP; - - if (has_cube_map) - dds_h.caps2 |= DDS_CUBE_MAP_ALLFACES; - - switch (format) { - case TXP_A8: - dds_h.ddspf = DDSPF_A8; - break; - case TXP_RGB8: - dds_h.ddspf = DDSPF_R8G8B8; - break; - case TXP_RGBA8: - dds_h.ddspf = DDSPF_A8R8G8B8; - break; - case TXP_RGB5: - dds_h.ddspf = DDSPF_R5G6B5; - break; - case TXP_RGB5A1: - dds_h.ddspf = DDSPF_A1R5G5B5; - break; - case TXP_RGBA4: - dds_h.ddspf = DDSPF_A4R4G4B4; - break; - case TXP_DXT1: - case TXP_DXT1a: - dds_h.ddspf = DDSPF_DXT1; - break; - case TXP_DXT3: - dds_h.ddspf = DDSPF_DXT3; - break; - case TXP_DXT5: - dds_h.ddspf = DDSPF_DXT5; - break; - case TXP_ATI1: - dds_h.ddspf = DDSPF_ATI1; - break; - case TXP_ATI2: - dds_h.ddspf = DDSPF_ATI2; - break; - case TXP_L8: - dds_h.ddspf = DDSPF_L8; - break; - case TXP_L8A8: - dds_h.ddspf = DDSPF_L8A8; - break; - } - s.write_uint32_t_reverse_endianness(DDS_MAGIC, true); s.write(&dds_h, sizeof(DDS_HEADER)); @@ -384,7 +384,7 @@ void dds::write(const wchar_t* path) { free_def(path_dds); } -static void dds_reverse_rgb(txp_format format, int64_t size, uint8_t* data) { +static void dds_reverse_rgb(txp_format format, size_t size, uint8_t* data) { uint8_t l, r, g, b, a; switch (format) { case TXP_RGB8: diff --git a/src/KKdLib/farc.cpp b/src/KKdLib/farc.cpp index 45082098..cbd3be7a 100644 --- a/src/KKdLib/farc.cpp +++ b/src/KKdLib/farc.cpp @@ -337,9 +337,8 @@ void farc::write(const wchar_t* path, farc_compress_mode mode, bool get_files) { if (!path) return; - if (get_files) { + if (get_files) files.clear(); - } wchar_t full_path_buf[MAX_PATH]; wchar_t* full_path = _wfullpath(full_path_buf, path, MAX_PATH); @@ -677,8 +676,14 @@ static errno_t farc_read_header(farc* f, stream& s) { s.set_position(0, SEEK_SET); f->signature = (farc_signature)s.read_uint32_t_reverse_endianness(true); - if (f->signature != FARC_FArc && f->signature != FARC_FArC && f->signature != FARC_FARC) + switch (f->signature) { + case FARC_FArc: + case FARC_FArC: + case FARC_FARC: + break; + default: return -2; + } f->ft = false; @@ -728,12 +733,13 @@ static errno_t farc_read_header(farc* f, stream& s) { length++; farc_file ff; - ff.name = std::string((const char*)dt, length); + ff.name.assign((const char*)dt, length); dt += length + 1; ff.offset = (size_t)load_reverse_endianness_uint32_t((void*)dt); ff.size_compressed = (size_t)load_reverse_endianness_uint32_t((void*)(dt + 4)); ff.size = (size_t)load_reverse_endianness_uint32_t((void*)(dt + 8)); ff.flags = (farc_flags)load_reverse_endianness_uint32_t((void*)(dt + 12)); + f->files.push_back(ff); dt += entry_size; files_count--; } @@ -783,7 +789,7 @@ static errno_t farc_read_header(farc* f, stream& s) { size_t length = 0; while (dt[length]) length++; - i.name = std::string((const char*)dt, length); + i.name.assign((const char*)dt, length); dt += length + 1; i.offset = (size_t)load_reverse_endianness_uint32_t((void*)dt); i.size_compressed = (size_t)load_reverse_endianness_uint32_t((void*)(dt + 4)); @@ -797,7 +803,7 @@ static errno_t farc_read_header(farc* f, stream& s) { size_t length = 0; while (dt[length]) length++; - i.name = std::string((const char*)dt, length); + i.name.assign((const char*)dt, length); dt += length + 1; i.offset = (size_t)load_reverse_endianness_uint32_t((void*)dt); i.size = (size_t)load_reverse_endianness_uint32_t((void*)(dt + 4)); @@ -880,11 +886,11 @@ static void farc_unpack_file(farc* f, stream& s, farc_file* ff, bool save, char* void* temp = force_malloc(temp_s); s.read(temp, temp_s); - void* t = temp; + size_t t = (size_t)temp; if (ff->flags & FARC_AES) if (f->ft) { temp_s -= 0x10; - t = (void*)((uint64_t)t + 0x10); + t += 0x10; aes128_ctx ctx; aes128_init_ctx_iv(&ctx, key_ft, (uint8_t*)temp); @@ -900,14 +906,14 @@ static void farc_unpack_file(farc* f, stream& s, farc_file* ff, bool save, char* if (ff->flags & FARC_GZIP) { ff->data_compressed = force_malloc(ff->size_compressed); - memcpy(ff->data_compressed, t, ff->size_compressed); + memcpy(ff->data_compressed, (void*)t, ff->size_compressed); deflate::decompress(ff->data_compressed, ff->size_compressed, &ff->data, &ff->size, deflate::MODE_GZIP); } else { ff->data_compressed = 0; ff->data = force_malloc(ff->size); - memcpy(ff->data, t, ff->size); + memcpy(ff->data, (void*)t, ff->size); } free_def(temp); } diff --git a/src/KKdLib/hash.cpp b/src/KKdLib/hash.cpp index ce5fb25b..a2683e67 100644 --- a/src/KKdLib/hash.cpp +++ b/src/KKdLib/hash.cpp @@ -175,21 +175,18 @@ uint32_t hash_murmurhash(const void* data, size_t size, hash ^= hash >> r; } - if (size > 0) { - if (size > 1) { - if (size > 2) { - b = d[2]; - if (b > 0x60 && b < 0x7B) - b -= 0x20; - hash += b << 16; - } - - b = d[1]; - if (b > 0x60 && b < 0x7B) - b -= 0x20; - hash += b << 8; - } - + switch (size) { + case 3: + b = d[2]; + if (b > 0x60 && b < 0x7B) + b -= 0x20; + hash += b << 16; + case 2: + b = d[1]; + if (b > 0x60 && b < 0x7B) + b -= 0x20; + hash += b << 8; + case 1: b = d[0]; if (b > 0x60 && b < 0x7B) b -= 0x20; @@ -197,6 +194,7 @@ uint32_t hash_murmurhash(const void* data, size_t size, hash *= m; hash ^= hash >> r; + break; } } diff --git a/src/KKdLib/image.cpp b/src/KKdLib/image.cpp index f9075725..f97b2109 100644 --- a/src/KKdLib/image.cpp +++ b/src/KKdLib/image.cpp @@ -5,9 +5,9 @@ #include "image.hpp" -static rgb565 rgb565_apply_color_tone(rgb565 col, color_tone* col_tone); +static rgb565 rgb565_apply_color_tone(rgb565 col, const color_tone* col_tone); static void rgb_to_ycc(vec3& rgb, vec3& ycc); -static void ycc_apply_col_tone_hue(vec3& ycc, color_tone* col_tone); +static void ycc_apply_col_tone_hue(vec3& ycc, const color_tone* col_tone); static void ycc_to_rgb(vec3& ycc, vec3& rgb); color_tone::color_tone() : hue(), saturation(), value(), contrast(), inverse() { @@ -15,7 +15,7 @@ color_tone::color_tone() : hue(), saturation(), value(), contrast(), inverse() { } void dxt1_image_apply_color_tone(int32_t width, int32_t height, - int32_t size, dxt1_block* data, color_tone* col_tone) { + int32_t size, dxt1_block* data, const color_tone* col_tone) { if (size <= 0) return; @@ -30,7 +30,7 @@ void dxt1_image_apply_color_tone(int32_t width, int32_t height, } void dxt5_image_apply_color_tone(int32_t width, int32_t height, - int32_t size, dxt5_block* data, color_tone* col_tone) { + int32_t size, dxt5_block* data, const color_tone* col_tone) { if (size <= 0) return; @@ -45,12 +45,12 @@ void dxt5_image_apply_color_tone(int32_t width, int32_t height, } void rgb565_image_apply_color_tone(int32_t width, int32_t height, - int32_t size, rgb565* data, color_tone* col_tone) { + int32_t size, rgb565* data, const color_tone* col_tone) { for (size_t i = (size - 1ULL) / sizeof(uint16_t) + 1; i; i--, data++) *data = rgb565_apply_color_tone(*data, col_tone); } -static rgb565 rgb565_apply_color_tone(rgb565 col, color_tone* col_tone) { +static rgb565 rgb565_apply_color_tone(rgb565 col, const color_tone* col_tone) { const vec3 scale = { (float_t)((1 << 5) - 1), (float_t)((1 << 6) - 1), @@ -106,7 +106,7 @@ inline static void rgb_to_ycc(vec3& rgb, vec3& ycc) { ycc.z = vec3::dot(rgb, _cr_coef_601); } -inline static void ycc_apply_col_tone_hue(vec3& ycc, color_tone* col_tone) { +inline static void ycc_apply_col_tone_hue(vec3& ycc, const color_tone* col_tone) { float_t hue = col_tone->hue * DEG_TO_RAD_FLOAT; float_t cos = cosf(hue); float_t sin = sinf(hue); diff --git a/src/KKdLib/image.hpp b/src/KKdLib/image.hpp index 16fb1e0c..6cf85674 100644 --- a/src/KKdLib/image.hpp +++ b/src/KKdLib/image.hpp @@ -42,8 +42,8 @@ struct dxt5_block { }; extern void dxt1_image_apply_color_tone(int32_t width, int32_t height, - int32_t size, dxt1_block* data, color_tone* col_tone); + int32_t size, dxt1_block* data, const color_tone* col_tone); extern void dxt5_image_apply_color_tone(int32_t width, int32_t height, - int32_t size, dxt5_block* data, color_tone* col_tone); + int32_t size, dxt5_block* data, const color_tone* col_tone); extern void rgb565_image_apply_color_tone(int32_t width, int32_t height, - int32_t size, rgb565* data, color_tone* col_tone); + int32_t size, rgb565* data, const color_tone* col_tone); diff --git a/src/KKdLib/key_val.cpp b/src/KKdLib/key_val.cpp index b75971e4..2e6bde28 100644 --- a/src/KKdLib/key_val.cpp +++ b/src/KKdLib/key_val.cpp @@ -1015,18 +1015,18 @@ void key_val_out::write(stream& s, std::string& key, vec3&& value) { write(s, key, *(vec3*)&value); } -void key_val_out::get_lexicographic_order(std::vector* vec, int32_t length) { - vec->clear(); +void key_val_out::get_lexicographic_order(std::vector& vec, int32_t length) { + vec.clear(); int32_t i, j, m; if (length < 1) return; - vec->reserve(length); + vec.reserve(length); j = 0; - vec->push_back(j); + vec.push_back(j); i = 1; j = 1; @@ -1036,7 +1036,7 @@ void key_val_out::get_lexicographic_order(std::vector* vec, int32_t len while (i < length) { if (j * 10 < m) { - vec->push_back(j); + vec.push_back(j); i++; j *= 10; } @@ -1048,7 +1048,7 @@ void key_val_out::get_lexicographic_order(std::vector* vec, int32_t len j++; } else if (j % 10 != 9) { - vec->push_back(j); + vec.push_back(j); i++; j++; } @@ -1062,7 +1062,7 @@ void key_val_out::get_lexicographic_order(std::vector* vec, int32_t len j++; } else if (j < length && j % 10 == 9) { - vec->push_back(j); + vec.push_back(j); i++; while (j % 10 == 9) j /= 10; @@ -1088,8 +1088,12 @@ static int64_t key_val_find_first_key(key_val* kv, int64_t low, int64_t high, full = true; } else { - res = str_utils_compare_length(s, s_len, l->str + offset, l->length - offset); - if (!res && s[l->length - offset]) + if (l->length >= offset) { + res = str_utils_compare_length(s, s_len, l->str + offset, l->length - offset); + if (!res && s[l->length - offset]) + res = 1; + } + else res = 1; full = false; } @@ -1124,8 +1128,12 @@ static int64_t key_val_find_last_key(key_val* kv, int64_t low, int64_t high, full = true; } else { - res = str_utils_compare_length(s, s_len, l->str + offset, l->length - offset); - if (!res && s[l->length - offset]) + if (l->length >= offset) { + res = str_utils_compare_length(s, s_len, l->str + offset, l->length - offset); + if (!res && s[l->length - offset]) + res = 1; + } + else res = 1; full = false; } diff --git a/src/KKdLib/key_val.hpp b/src/KKdLib/key_val.hpp index d274149b..8ec0141d 100644 --- a/src/KKdLib/key_val.hpp +++ b/src/KKdLib/key_val.hpp @@ -140,5 +140,5 @@ struct key_val_out { void write(stream& s, const char* key, vec3&& value); void write(stream& s, std::string& key, vec3&& value); - static void get_lexicographic_order(std::vector* vec, int32_t length); + static void get_lexicographic_order(std::vector& vec, int32_t length); }; diff --git a/src/KKdLib/mat.cpp b/src/KKdLib/mat.cpp index c88c7415..9191946d 100644 --- a/src/KKdLib/mat.cpp +++ b/src/KKdLib/mat.cpp @@ -76,6 +76,21 @@ inline void mat3_mult(const mat3* x, const mat3* y, mat3* z) { z->row2 = *(vec3*)&zt; } +inline void mat3_mult_vec2(const mat3* x, const vec2* y, vec2* z) { + __m128 xt; + __m128 yt; + __m128 zt; + __m128 zt0; + __m128 zt1; + *(vec2*)&yt = *y; + *(vec3*)&xt = x->row0; + zt0 = _mm_mul_ps(xt, _mm_shuffle_ps(yt, yt, 0x00)); + *(vec3*)&xt = x->row1; + zt1 = _mm_mul_ps(xt, _mm_shuffle_ps(yt, yt, 0x55)); + zt = _mm_add_ps(zt0, zt1); + *z = *(vec2*)&zt; +} + inline void mat3_mult_vec(const mat3* x, const vec3* y, vec3* z) { __m128 xt; __m128 yt; @@ -593,10 +608,57 @@ inline void mat3_from_quat(const quat* quat, mat3* mat) { } inline void mat3_from_axis_angle(const vec3* axis, float_t angle, mat3* mat) { - quat quat; + float_t angle_sin; + float_t angle_cos; + float_t angle_cos_1; + vec3 _axis; + vec3 _axis_sin; + vec3 temp; - quat_from_axis_angle(axis, angle, &quat); - mat3_from_quat(&quat, mat); + angle_sin = sinf(angle); + angle_cos = cosf(angle); + angle_cos_1 = 1.0f - angle_cos; + + _axis = vec3::normalize(*axis); + + _axis_sin = _axis * angle_sin; + temp = _axis * (_axis.x * angle_cos_1); + mat->row0.x = temp.x + angle_cos; + mat->row1.x = temp.y - _axis_sin.z; + mat->row2.x = temp.z + _axis_sin.y; + temp = _axis * (_axis.y * angle_cos_1); + mat->row0.y = temp.x + _axis_sin.z; + mat->row1.y = temp.y + angle_cos; + mat->row2.y = temp.z - _axis_sin.x; + temp = _axis * (_axis.z * angle_cos_1); + mat->row0.z = temp.x - _axis_sin.y; + mat->row1.z = temp.y + _axis_sin.x; + mat->row2.z = temp.z + angle_cos; +} + +inline void mat3_from_axis_angle_sin_cos(const vec3* axis, float_t sin_val, float_t cos_val, mat3* mat) { + float_t cos_val_1; + vec3 _axis; + vec3 _axis_sin; + vec3 temp; + + cos_val_1 = 1.0f - cos_val; + + _axis = vec3::normalize(*axis); + + _axis_sin = _axis * sin_val; + temp = _axis * (_axis.x * cos_val_1); + mat->row0.x = temp.x + cos_val; + mat->row1.x = temp.y - _axis_sin.z; + mat->row2.x = temp.z + _axis_sin.y; + temp = _axis * (_axis.y * cos_val_1); + mat->row0.y = temp.x + _axis_sin.z; + mat->row1.y = temp.y + cos_val; + mat->row2.y = temp.z - _axis_sin.x; + temp = _axis * (_axis.z * cos_val_1); + mat->row0.z = temp.x - _axis_sin.y; + mat->row1.z = temp.y + _axis_sin.x; + mat->row2.z = temp.z + cos_val; } inline void mat3_from_mat4(const mat4* x, mat3* z) { @@ -722,6 +784,32 @@ inline void mat4_mult(const mat4* x, const mat4* y, mat4* z) { z->row3 = vec4::store_xmm(_mm_add_ps(_mm_add_ps(t0, t1), _mm_add_ps(t2, t3))); } +inline void mat4_mult_vec2(const mat4* x, const vec2* y, vec2* z) { + __m128 yt; + __m128 zt; + __m128 zt0; + __m128 zt1; + *(vec2*)&yt = *y; + zt0 = _mm_mul_ps(vec4::load_xmm(x->row0), _mm_shuffle_ps(yt, yt, 0x00)); + zt1 = _mm_mul_ps(vec4::load_xmm(x->row1), _mm_shuffle_ps(yt, yt, 0x55)); + zt = _mm_add_ps(zt0, zt1); + *z = *(vec2*)&zt; +} + +inline void mat4_mult_vec2_trans(const mat4* x, const vec2* y, vec2* z) { + __m128 yt; + __m128 zt; + __m128 zt0; + __m128 zt1; + __m128 zt2; + *(vec2*)&yt = *y; + zt0 = _mm_mul_ps(vec4::load_xmm(x->row0), _mm_shuffle_ps(yt, yt, 0x00)); + zt1 = _mm_mul_ps(vec4::load_xmm(x->row1), _mm_shuffle_ps(yt, yt, 0x55)); + zt2 = vec4::load_xmm(x->row3); + zt = _mm_add_ps(_mm_add_ps(zt0, zt1), zt2); + *z = *(vec2*)&zt; +} + inline void mat4_mult_vec3(const mat4* x, const vec3* y, vec3* z) { __m128 yt; __m128 zt; @@ -1613,6 +1701,35 @@ inline void mat4_from_axis_angle(const vec3* axis, float_t angle, mat4* mat) { mat->row3 = { 0.0f, 0.0f, 0.0f, 1.0f }; } +inline void mat4_from_axis_angle_sin_cos(const vec3* axis, float_t sin_val, float_t cos_val, mat4* mat) { + float_t cos_val_1; + vec3 _axis; + vec3 _axis_sin; + vec3 temp; + + cos_val_1 = 1.0f - cos_val; + + _axis = vec3::normalize(*axis); + + _axis_sin = _axis * sin_val; + temp = _axis * (_axis.x * cos_val_1); + mat->row0.x = temp.x + cos_val; + mat->row1.x = temp.y - _axis_sin.z; + mat->row2.x = temp.z + _axis_sin.y; + temp = _axis * (_axis.y * cos_val_1); + mat->row0.y = temp.x + _axis_sin.z; + mat->row1.y = temp.y + cos_val; + mat->row2.y = temp.z - _axis_sin.x; + temp = _axis * (_axis.z * cos_val_1); + mat->row0.z = temp.x - _axis_sin.y; + mat->row1.z = temp.y + _axis_sin.x; + mat->row2.z = temp.z + cos_val; + mat->row0.w = 0.0f; + mat->row1.w = 0.0f; + mat->row2.w = 0.0f; + mat->row3 = { 0.0f, 0.0f, 0.0f, 1.0f }; +} + inline void mat4_from_mat3(const mat3* x, mat4* z) { *(vec3*)&z->row0 = x->row0; z->row0.w = 0.0f; diff --git a/src/KKdLib/mat.hpp b/src/KKdLib/mat.hpp index c4a1ed25..4649e2f3 100644 --- a/src/KKdLib/mat.hpp +++ b/src/KKdLib/mat.hpp @@ -48,6 +48,7 @@ extern const mat4 mat4_null; extern void mat3_add(const mat3* x, const mat3* y, mat3* z); extern void mat3_sub(const mat3* x, const mat3* y, mat3* z); extern void mat3_mult(const mat3* x, const mat3* y, mat3* z); +extern void mat3_mult_vec2(const mat3* x, const vec2* y, vec2* z); extern void mat3_mult_vec(const mat3* x, const vec3* y, vec3* z); extern void mat3_mult_scalar(const mat3* x, float_t y, mat3* z); extern void mat3_transpose(const mat3* x, mat3* z); @@ -81,6 +82,7 @@ extern void mat3_scale_y_mult(const mat3* x, float_t y, mat3* z); extern void mat3_scale_z_mult(const mat3* x, float_t y, mat3* z); extern void mat3_from_quat(const quat* quat, mat3* mat); extern void mat3_from_axis_angle(const vec3* axis, float_t angle, mat3* mat); +extern void mat3_from_axis_angle_sin_cos(const vec3* axis, float_t sin_val, float_t cos_val, mat3* mat); extern void mat3_from_mat4(const mat4* x, mat3* z); extern void mat3_from_mat4_inverse(const mat4* x, mat3* z); extern void mat3_get_rotation(const mat3* x, vec3* z); @@ -91,6 +93,8 @@ extern void mat3_mult_axis_angle(const mat3* x, const vec3* axis, const float_t extern void mat4_add(const mat4* x, const mat4* y, mat4* z); extern void mat4_sub(const mat4* x, const mat4* y, mat4* z); extern void mat4_mult(const mat4* x, const mat4* y, mat4* z); +extern void mat4_mult_vec2(const mat4* x, const vec2* y, vec2* z); +extern void mat4_mult_vec2_trans(const mat4* x, const vec2* y, vec2* z); extern void mat4_mult_vec3(const mat4* x, const vec3* y, vec3* z); extern void mat4_mult_vec3_inv(const mat4* x, const vec3* y, vec3* z); extern void mat4_mult_vec3_trans(const mat4* x, const vec3* y, vec3* z); @@ -147,6 +151,7 @@ extern void mat4_translate_y_add(const mat4* x, float_t y, mat4* z); extern void mat4_translate_z_add(const mat4* x, float_t y, mat4* z); extern void mat4_from_quat(const quat* quat, mat4* mat); extern void mat4_from_axis_angle(const vec3* axis, float_t angle, mat4* mat); +extern void mat4_from_axis_angle_sin_cos(const vec3* axis, float_t sin_val, float_t cos_val, mat4* mat); extern void mat4_from_mat3(const mat3* x, mat4* z); extern void mat4_from_mat3_inverse(const mat3* x, mat4* z); extern void mat4_clear_rot(mat4* x, mat4* z); diff --git a/src/KKdLib/mot.cpp b/src/KKdLib/mot.cpp index ae237d33..a1ae0f20 100644 --- a/src/KKdLib/mot.cpp +++ b/src/KKdLib/mot.cpp @@ -30,12 +30,12 @@ struct mot_header_modern { int32_t bone_info_count; }; -static void mot_classic_read_inner(mot_set* ms, prj::shared_ptr& alloc, stream& s); +static void mot_classic_read_inner(mot_set* ms, prj::shared_ptr& alloc, stream& s); static void mot_classic_write_inner(mot_set* ms, stream& s); -static void mot_modern_read_inner(mot_set* ms, prj::shared_ptr& alloc, stream& s); +static void mot_modern_read_inner(mot_set* ms, prj::shared_ptr& alloc, stream& s); static void mot_modern_write_inner(mot_set* ms, stream& s); static const char* mot_read_utf8_string_null_terminated_offset( - prj::shared_ptr& alloc, stream& s, int64_t offset); + prj::shared_ptr& alloc, stream& s, int64_t offset); mot_bone_info::mot_bone_info() : name(), index() { @@ -67,7 +67,7 @@ void mot_set::pack_file(void** data, size_t* size) { s.copy(data, size); } -void mot_set::unpack_file(prj::shared_ptr alloc, const void* data, size_t size, bool modern) { +void mot_set::unpack_file(prj::shared_ptr alloc, const void* data, size_t size, bool modern) { if (!data || !size) return; @@ -143,7 +143,7 @@ inline static float_t mot_set_add_key(bool has_error, float_t* a, int32_t frame, } mot_key_set_type mot_set::fit_keys_into_curve(std::vector& values_src, - prj::shared_ptr alloc, uint16_t*& frames, float_t*& values, size_t& keys_count) { + prj::shared_ptr alloc, uint16_t*& frames, float_t*& values, size_t& keys_count) { std::vector _frames; std::vector _values; mot_key_set_type type = mot_set::fit_keys_into_curve(values_src, _frames, _values); @@ -331,7 +331,7 @@ mot_key_set_type mot_set::fit_keys_into_curve(std::vector& values_src, return MOT_KEY_SET_HERMITE; } -static void mot_classic_read_inner(mot_set* ms, prj::shared_ptr& alloc, stream& s) { +static void mot_classic_read_inner(mot_set* ms, prj::shared_ptr& alloc, stream& s) { size_t count = 0; while (s.read_uint64_t() != 0) { s.read_uint64_t(); @@ -519,7 +519,7 @@ static void mot_classic_write_inner(mot_set* ms, stream& s) { free_def(mh); } -static void mot_modern_read_inner(mot_set* ms, prj::shared_ptr& alloc, stream& s) { +static void mot_modern_read_inner(mot_set* ms, prj::shared_ptr& alloc, stream& s) { f2_struct st; st.read(s); if (st.header.signature != reverse_endianness_uint32_t('MOTC') || !st.data.size()) { @@ -959,7 +959,7 @@ static void mot_modern_write_inner(mot_set* ms, stream& s) { } inline static const char* mot_read_utf8_string_null_terminated_offset( - prj::shared_ptr& alloc, stream& s, int64_t offset) { + prj::shared_ptr& alloc, stream& s, int64_t offset) { size_t len = s.read_utf8_string_null_terminated_offset_length(offset); char* str = alloc->allocate(len + 1); s.position_push(offset, SEEK_SET); diff --git a/src/KKdLib/mot.hpp b/src/KKdLib/mot.hpp index f55667dc..088d8836 100644 --- a/src/KKdLib/mot.hpp +++ b/src/KKdLib/mot.hpp @@ -8,7 +8,7 @@ #include #include #include "prj/shared_ptr.hpp" -#include "alloc_data.hpp" +#include "prj/stack_allocator.hpp" #include "default.hpp" #include "vec.hpp" @@ -76,10 +76,10 @@ struct mot_set { mot_set(); void pack_file(void** data, size_t* size); - void unpack_file(prj::shared_ptr alloc, const void* data, size_t size, bool modern); + void unpack_file(prj::shared_ptr alloc, const void* data, size_t size, bool modern); static mot_key_set_type fit_keys_into_curve(std::vector& values_src, - prj::shared_ptr alloc, uint16_t*& frames, float_t*& values, size_t& keys_count); + prj::shared_ptr alloc, uint16_t*& frames, float_t*& values, size_t& keys_count); static mot_key_set_type fit_keys_into_curve(std::vector& values_src, std::vector& frames, std::vector& values); }; diff --git a/src/KKdLib/obj.cpp b/src/KKdLib/obj.cpp index 14ac2e67..ddac53fa 100644 --- a/src/KKdLib/obj.cpp +++ b/src/KKdLib/obj.cpp @@ -115,76 +115,77 @@ static bool obj_material_texture_enrs_table_initialized; static void obj_material_texture_enrs_table_init(); static void obj_material_texture_enrs_table_free(void); -static void obj_vertex_add_bone_weight(vec4& bone_weight, vec4i& bone_index, int32_t index, float_t weight); +static void obj_vertex_add_bone_weight(vec4& bone_weight, vec4i16& bone_index, int16_t index, float_t weight); static void obj_vertex_generate_tangents(obj_mesh* mesh); -static void obj_vertex_validate_bone_data(vec4& bone_weight, vec4i& bone_index); +static void obj_vertex_validate_bone_data(vec4& bone_weight, vec4i16& bone_index); static void obj_move_data(obj* obj_dst, const obj* obj_src, - prj::shared_ptr alloc); + prj::shared_ptr alloc); static void obj_move_data_mesh(obj_mesh* mesh_dst, const obj_mesh* mesh_src, - prj::shared_ptr alloc); + prj::shared_ptr alloc); static void obj_move_data_sub_mesh(obj_sub_mesh* sub_mesh_dst, const obj_sub_mesh* sub_mesh_src, - prj::shared_ptr alloc); + prj::shared_ptr alloc); static obj_skin* obj_move_data_skin(const obj_skin* sk_src, - prj::shared_ptr alloc); + prj::shared_ptr alloc); static void obj_move_data_skin_bone(obj_skin_bone* bone_dst, const obj_skin_bone* bone_src, - prj::shared_ptr alloc); + prj::shared_ptr alloc); static obj_skin_ex_data* obj_move_data_skin_ex_data(const obj_skin_ex_data* ex_src, - prj::shared_ptr alloc); + prj::shared_ptr alloc); static obj_skin_block_cloth* obj_move_data_skin_block_cloth(const obj_skin_block_cloth* cls_src, - prj::shared_ptr alloc); + prj::shared_ptr alloc); static void obj_move_data_skin_block_cloth_root(obj_skin_block_cloth_root* cloth_root_dst, const obj_skin_block_cloth_root* cloth_root_src, - prj::shared_ptr alloc); + prj::shared_ptr alloc); static void obj_move_data_skin_block_cloth_root_bone_weight(obj_skin_block_cloth_root_bone_weight* bone_weight_dst, const obj_skin_block_cloth_root_bone_weight* bone_weight_src, - prj::shared_ptr alloc); + prj::shared_ptr alloc); static obj_skin_block_constraint* obj_move_data_skin_block_constraint(const obj_skin_block_constraint* cns_src, - prj::shared_ptr alloc); + prj::shared_ptr alloc); static void obj_move_data_skin_block_constraint_up_vector(obj_skin_block_constraint_up_vector* up_vector_dst, const obj_skin_block_constraint_up_vector* up_vector_src, - prj::shared_ptr alloc); + prj::shared_ptr alloc); static obj_skin_block_expression* obj_move_data_skin_block_expression(const obj_skin_block_expression* exp_src, - prj::shared_ptr alloc); + prj::shared_ptr alloc); static obj_skin_block_motion* obj_move_data_skin_block_motion(const obj_skin_block_motion* mot_src, - prj::shared_ptr alloc); + prj::shared_ptr alloc); static void obj_move_data_skin_block_node(obj_skin_block_node* node_dst, const obj_skin_block_node* node_src, - prj::shared_ptr alloc); + prj::shared_ptr alloc); static obj_skin_block_osage* obj_move_data_skin_block_osage(const obj_skin_block_osage* osg_src, - prj::shared_ptr alloc); + prj::shared_ptr alloc); static obj_skin_skin_param* obj_move_data_skin_param(const obj_skin_skin_param* skp_src, - prj::shared_ptr alloc); + prj::shared_ptr alloc); -static void obj_set_classic_read_inner(obj_set* set, prj::shared_ptr& alloc, stream& s); +static void obj_set_classic_read_inner(obj_set* set, prj::shared_ptr& alloc, stream& s); static void obj_set_classic_write_inner(obj_set* set, stream& s); -static void obj_classic_read_index(obj_sub_mesh* sub_mesh, prj::shared_ptr alloc, stream& s); +static void obj_classic_read_index(obj_sub_mesh* sub_mesh, prj::shared_ptr alloc, stream& s); static void obj_classic_write_index(obj_sub_mesh* sub_mesh, stream& s); -static void obj_classic_read_model(obj* obj, prj::shared_ptr alloc, stream& s, int64_t base_offset); +static void obj_classic_read_model(obj* obj, prj::shared_ptr alloc, + stream& s, int64_t base_offset); static void obj_classic_write_model(obj* obj, stream& s, int64_t base_offset); static void obj_classic_read_model_mesh(obj_mesh* mesh, - prj::shared_ptr alloc, stream& s, int64_t base_offset); + prj::shared_ptr alloc, stream& s, int64_t base_offset); static void obj_classic_read_model_sub_mesh(obj_sub_mesh* sub_mesh, - prj::shared_ptr alloc, stream& s, int64_t base_offset); -static obj_skin* obj_classic_read_skin(prj::shared_ptr alloc, stream& s, int64_t base_offset); + prj::shared_ptr alloc, stream& s, int64_t base_offset); +static obj_skin* obj_classic_read_skin(prj::shared_ptr alloc, stream& s, int64_t base_offset); static void obj_classic_write_skin(obj_skin* sk, stream& s, int64_t base_offset); -static obj_skin_ex_data* obj_classic_read_skin_ex_data(prj::shared_ptr alloc, +static obj_skin_ex_data* obj_classic_read_skin_ex_data(prj::shared_ptr alloc, stream& s, int64_t base_offset); static obj_skin_block_cloth* obj_classic_read_skin_block_cloth( - prj::shared_ptr alloc, stream& s, const char** str); + prj::shared_ptr alloc, stream& s, const char** str); static void obj_classic_write_skin_block_cloth(obj_skin_block_cloth* cls, stream& s, std::vector& strings, std::vector& string_offsets, int64_t* mat_array_offset, int64_t* root_array_offset, int64_t* node_array_offset, int64_t* mesh_index_array_offset, int64_t* backface_mesh_index_array_offset); static void obj_classic_read_skin_block_cloth_root(obj_skin_block_cloth_root* cloth_root, - prj::shared_ptr alloc, stream& s, const char** str); + prj::shared_ptr alloc, stream& s, const char** str); static void obj_classic_write_skin_block_cloth_root(obj_skin_block_cloth_root* cloth_root, stream& s, std::vector& strings, std::vector& string_offsets); static void obj_classic_read_skin_block_cloth_root_bone_weight(obj_skin_block_cloth_root_bone_weight* bone_weight, - prj::shared_ptr alloc, stream& s, const char** str); + prj::shared_ptr alloc, stream& s, const char** str); static void obj_classic_write_skin_block_cloth_root_bone_weight(obj_skin_block_cloth_root_bone_weight* bone_weight, stream& s, std::vector& strings, std::vector& string_offsets); static obj_skin_block_constraint* obj_classic_read_skin_block_constraint( - prj::shared_ptr alloc, stream& s, const char** str); + prj::shared_ptr alloc, stream& s, const char** str); static void obj_classic_write_skin_block_constraint(obj_skin_block_constraint* cns, stream& s, std::vector& strings, std::vector& string_offsets, const char** bone_name_array, int64_t* offsets); @@ -193,74 +194,75 @@ static void obj_classic_read_skin_block_constraint_attach_point(obj_skin_block_c static void obj_classic_write_skin_block_constraint_attach_point(obj_skin_block_constraint_attach_point* attach_point, stream& s, std::vector& strings, std::vector& string_offsets); static void obj_classic_read_skin_block_constraint_up_vector(obj_skin_block_constraint_up_vector* up_vector, - prj::shared_ptr alloc, stream& s, const char** str); + prj::shared_ptr alloc, stream& s, const char** str); static void obj_classic_write_skin_block_constraint_up_vector(obj_skin_block_constraint_up_vector* up_vector, stream& s, std::vector& strings, std::vector& string_offsets); static obj_skin_block_expression* obj_classic_read_skin_block_expression( - prj::shared_ptr alloc, stream& s, const char** str); + prj::shared_ptr alloc, stream& s, const char** str); static void obj_classic_write_skin_block_expression(obj_skin_block_expression* exp, stream& s, std::vector& strings, std::vector& string_offsets, const char** bone_name_array); static obj_skin_block_motion* obj_classic_read_skin_block_motion( - prj::shared_ptr alloc, stream& s, const char** str); + prj::shared_ptr alloc, stream& s, const char** str); static void obj_classic_write_skin_block_motion(obj_skin_block_motion* mot, stream& s, std::vector& strings, std::vector& string_offsets, const char** bone_name_array, int64_t* bone_name_array_offset, int64_t* bone_matrix_array_offset); static void obj_classic_read_skin_block_node(obj_skin_block_node* node, - prj::shared_ptr alloc, stream& s, const char** str); + prj::shared_ptr alloc, stream& s, const char** str); static void obj_classic_write_skin_block_node(obj_skin_block_node* node, stream& s, std::vector& strings, std::vector& string_offsets); static obj_skin_block_osage* obj_classic_read_skin_block_osage( - prj::shared_ptr alloc, stream& s, const char** str); + prj::shared_ptr alloc, stream& s, const char** str); static void obj_classic_write_skin_block_osage(obj_skin_block_osage* osg, stream& s, std::vector& strings, std::vector& string_offsets, int64_t* node_array_offset); static obj_skin_skin_param* obj_classic_read_skin_param( - prj::shared_ptr alloc, stream& s, const char** str); + prj::shared_ptr alloc, stream& s, const char** str); static void obj_classic_write_skin_param(obj_skin_skin_param* skp, stream& s, std::vector& strings, std::vector& string_offsets); static void obj_classic_read_vertex(obj_mesh* mesh, - prj::shared_ptr alloc, stream& s, int64_t* vertex, int64_t base_offset, uint32_t num_vertex, + prj::shared_ptr alloc, stream& s, int64_t* vertex, int64_t base_offset, uint32_t num_vertex, obj_vertex_format_file vertex_format_file); static void obj_classic_write_vertex(obj_mesh* mesh, stream& s, int64_t* vertex, int64_t base_offset, uint32_t* num_vertex, obj_vertex_format_file* vertex_format_file, uint32_t* size_vertex); -static void obj_set_modern_read_inner(obj_set* set, prj::shared_ptr& alloc, stream& s); +static void obj_set_modern_read_inner(obj_set* set, prj::shared_ptr& alloc, stream& s); static void obj_set_modern_write_inner(obj_set* set, stream& s); -static void obj_modern_read_index(obj_sub_mesh* sub_mesh, prj::shared_ptr alloc, stream& s); +static void obj_modern_read_index(obj_sub_mesh* sub_mesh, prj::shared_ptr alloc, stream& s); static void obj_modern_write_index(obj_sub_mesh* sub_mesh, stream& s, bool is_x, f2_struct* ovtx); -static void obj_modern_read_model(obj* obj, prj::shared_ptr alloc, stream& s, int64_t base_offset, +static void obj_modern_read_model(obj* obj, prj::shared_ptr alloc, + stream& s, int64_t base_offset, uint32_t header_length, bool is_x, stream* s_oidx, stream* s_ovtx); static void obj_modern_write_model(obj* obj, stream& s, int64_t base_offset, bool is_x, f2_struct* omdl); static void obj_modern_read_model_mesh(obj_mesh* mesh, - prj::shared_ptr alloc, stream& s, int64_t base_offset, + prj::shared_ptr alloc, stream& s, int64_t base_offset, uint32_t header_length, bool is_x, stream* s_oidx, stream* s_ovtx); static void obj_modern_read_model_sub_mesh(obj_sub_mesh* sub_mesh, - prj::shared_ptr alloc, stream& s, int64_t base_offset, + prj::shared_ptr alloc, stream& s, int64_t base_offset, uint32_t header_length, bool is_x, stream* s_oidx); -static obj_skin* obj_modern_read_skin(prj::shared_ptr alloc, +static obj_skin* obj_modern_read_skin(prj::shared_ptr alloc, stream& s, int64_t base_offset, uint32_t header_length, bool is_x); static void obj_modern_write_skin(obj_skin* sk, stream& s, int64_t base_offset, bool is_x, f2_struct* oskn); -static obj_skin_ex_data* obj_modern_read_skin_ex_data(prj::shared_ptr alloc, +static obj_skin_ex_data* obj_modern_read_skin_ex_data(prj::shared_ptr alloc, stream& s, int64_t base_offset, uint32_t header_length, bool is_x); static obj_skin_block_cloth* obj_modern_read_skin_block_cloth( - prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x); + prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x); static void obj_modern_write_skin_block_cloth(obj_skin_block_cloth* cls, stream& s, std::vector& strings, std::vector& string_offsets, bool is_x, int64_t* mat_array_offset, int64_t* root_array_offset, int64_t* node_array_offset, int64_t* mesh_index_array_offset, int64_t* backface_mesh_index_array_offset); static void obj_modern_read_skin_block_cloth_root(obj_skin_block_cloth_root* cloth_root, - prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x); + prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x); static void obj_modern_write_skin_block_cloth_root(obj_skin_block_cloth_root* cloth_root, stream& s, std::vector& strings, std::vector& string_offsets, bool is_x); static void obj_modern_read_skin_block_cloth_root_bone_weight(obj_skin_block_cloth_root_bone_weight* bone_weight, - prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x); + prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x); static void obj_modern_write_skin_block_cloth_root_bone_weight(obj_skin_block_cloth_root_bone_weight* bone_weight, stream& s, std::vector& strings, std::vector& string_offsets, bool is_x); static obj_skin_block_constraint* obj_modern_read_skin_block_constraint( - prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x); + prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x); static void obj_modern_write_skin_block_constraint(obj_skin_block_constraint* cns, stream& s, std::vector& strings, std::vector& string_offsets, const char** bone_name_array, bool is_x, int64_t* offsets); @@ -269,37 +271,37 @@ static void obj_modern_read_skin_block_constraint_attach_point(obj_skin_block_co static void obj_modern_write_skin_block_constraint_attach_point(obj_skin_block_constraint_attach_point* attach_point, stream& s, std::vector& strings, std::vector& string_offsets, bool is_x); static void obj_modern_read_skin_block_constraint_up_vector(obj_skin_block_constraint_up_vector* up_vector, - prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x); + prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x); static void obj_modern_write_skin_block_constraint_up_vector(obj_skin_block_constraint_up_vector* up_vector, stream& s, std::vector& strings, std::vector& string_offsets, bool is_x); static obj_skin_block_expression* obj_modern_read_skin_block_expression( - prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x); + prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x); static void obj_modern_write_skin_block_expression(obj_skin_block_expression* exp, stream& s, std::vector& strings, std::vector& string_offsets, const char** bone_name_array, bool is_x); static obj_skin_block_motion* obj_modern_read_skin_block_motion( - prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x); + prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x); static void obj_modern_write_skin_block_motion(obj_skin_block_motion* mot, stream& s, std::vector& strings, std::vector& string_offsets, bool is_x, const char** bone_name_array, int64_t* bone_name_array_offset, int64_t* bone_matrix_array_offset); static void obj_modern_read_skin_block_node(obj_skin_block_node* node, - prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x); + prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x); static void obj_modern_write_skin_block_node(obj_skin_block_node* node, stream& s, std::vector& strings, std::vector& string_offsets, bool is_x); static obj_skin_block_osage* obj_modern_read_skin_block_osage( - prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x); + prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x); static void obj_modern_write_skin_block_osage(obj_skin_block_osage* osg, stream& s, std::vector& strings, std::vector& string_offsets, bool is_x); -static void obj_modern_read_vertex(obj_mesh* mesh, prj::shared_ptr alloc, stream& s, +static void obj_modern_read_vertex(obj_mesh* mesh, prj::shared_ptr alloc, stream& s, int64_t* vertex, const uint32_t vertex_format_index, uint32_t num_vertex, uint32_t size_vertex); static void obj_modern_write_vertex(obj_mesh* mesh, stream& s, int64_t* vertex, uint32_t* vertex_format_index, uint32_t* num_vertex, uint32_t* size_vertex, f2_struct* ovtx); static const char* obj_move_data_string(const char* str, - prj::shared_ptr& alloc); + prj::shared_ptr& alloc); static const char* obj_read_utf8_string_null_terminated_offset( - prj::shared_ptr& alloc, stream& s, int64_t offset); + prj::shared_ptr& alloc, stream& s, int64_t offset); static uint32_t obj_skin_strings_get_string_index(std::vector& vec, const char* str); static int64_t obj_skin_strings_get_string_offset(std::vector& vec, @@ -322,7 +324,7 @@ obj_bounding_sphere::obj_bounding_sphere() : radius() { } -obj_material_shader_lighting_type obj_material_shader_attrib::get_lighting_type() { +obj_material_shader_lighting_type obj_material_shader_attrib::get_lighting_type() const { if (!m.is_lgt_diffuse && !m.is_lgt_specular) return OBJ_MATERIAL_SHADER_LIGHTING_CONSTANT; else if (!m.is_lgt_specular) @@ -331,7 +333,7 @@ obj_material_shader_lighting_type obj_material_shader_attrib::get_lighting_type( return OBJ_MATERIAL_SHADER_LIGHTING_PHONG; } -int32_t obj_texture_attrib::get_blend() { +int32_t obj_texture_attrib::get_blend() const { switch (m.blend) { case 4: return 2; @@ -509,7 +511,7 @@ obj_num(), tex_id_data(), tex_id_num(), reserved() { } -void obj_set::move_data(obj_set* set_src, prj::shared_ptr alloc) { +void obj_set::move_data(obj_set* set_src, prj::shared_ptr alloc) { if (!set_src->ready) { ready = false; modern = false; @@ -554,7 +556,7 @@ void obj_set::pack_file(void** data, size_t* size) { s.copy(data, size); } -void obj_set::unpack_file(prj::shared_ptr alloc, const void* data, size_t size, bool modern) { +void obj_set::unpack_file(prj::shared_ptr alloc, const void* data, size_t size, bool modern) { if (!data || !size) return; @@ -583,7 +585,7 @@ static void obj_material_texture_enrs_table_free(void) { obj_material_texture_enrs_table_initialized = false; } -static void obj_vertex_add_bone_weight(vec4& bone_weight, vec4i& bone_index, int32_t index, float_t weight) { +static void obj_vertex_add_bone_weight(vec4& bone_weight, vec4i16& bone_index, int16_t index, float_t weight) { if (bone_index.x < 0) { bone_index.x = index; bone_weight.x = weight; @@ -761,20 +763,20 @@ static void obj_vertex_generate_tangents(obj_mesh* mesh) { enum_or(mesh->vertex_format, OBJ_VERTEX_TANGENT); } -static void obj_vertex_validate_bone_data(vec4& bone_weight, vec4i& bone_index) { +static void obj_vertex_validate_bone_data(vec4& bone_weight, vec4i16& bone_index) { vec4 _bone_weight = { 0.0f, 0.0f, 0.0f, 0.0f }; - vec4i _bone_index = { -1, -1, -1, -1 }; + vec4i16 _bone_index = { -1, -1, -1, -1 }; - if (bone_weight.x > 0) + if (bone_index.x >= 0 && bone_weight.x > 0.0f) obj_vertex_add_bone_weight(_bone_weight, _bone_index, bone_index.x, bone_weight.x); - if (bone_weight.y > 0) + if (bone_index.y >= 0 && bone_weight.y > 0.0f) obj_vertex_add_bone_weight(_bone_weight, _bone_index, bone_index.y, bone_weight.y); - if (bone_weight.z > 0) + if (bone_index.z >= 0 && bone_weight.z > 0.0f) obj_vertex_add_bone_weight(_bone_weight, _bone_index, bone_index.z, bone_weight.z); - if (bone_weight.w > 0) + if (bone_index.w >= 0 && bone_weight.w > 0.0f) obj_vertex_add_bone_weight(_bone_weight, _bone_index, bone_index.w, bone_weight.w); float_t sum = _bone_weight.x + _bone_weight.y + _bone_weight.z + _bone_weight.w; @@ -786,7 +788,7 @@ static void obj_vertex_validate_bone_data(vec4& bone_weight, vec4i& bone_index) } static void obj_move_data(obj* obj_dst, const obj* obj_src, - prj::shared_ptr alloc) { + prj::shared_ptr alloc) { obj_dst->bounding_sphere = obj_src->bounding_sphere; uint32_t num_mesh = obj_src->num_mesh; @@ -814,7 +816,7 @@ static void obj_move_data(obj* obj_dst, const obj* obj_src, } static void obj_move_data_mesh(obj_mesh* mesh_dst, const obj_mesh* mesh_src, - prj::shared_ptr alloc) { + prj::shared_ptr alloc) { mesh_dst->flags = mesh_src->flags; mesh_dst->bounding_sphere = mesh_src->bounding_sphere; @@ -841,7 +843,7 @@ static void obj_move_data_mesh(obj_mesh* mesh_dst, const obj_mesh* mesh_src, } static void obj_move_data_sub_mesh(obj_sub_mesh* sub_mesh_dst, const obj_sub_mesh* sub_mesh_src, - prj::shared_ptr alloc) { + prj::shared_ptr alloc) { sub_mesh_dst->flags = sub_mesh_src->flags; sub_mesh_dst->bounding_sphere = sub_mesh_src->bounding_sphere; sub_mesh_dst->material_index = sub_mesh_src->material_index; @@ -868,7 +870,7 @@ static void obj_move_data_sub_mesh(obj_sub_mesh* sub_mesh_dst, const obj_sub_mes } static obj_skin* obj_move_data_skin(const obj_skin* sk_src, - prj::shared_ptr alloc) { + prj::shared_ptr alloc) { if (!sk_src) return 0; @@ -889,7 +891,7 @@ static obj_skin* obj_move_data_skin(const obj_skin* sk_src, } static void obj_move_data_skin_bone(obj_skin_bone* bone_dst, const obj_skin_bone* bone_src, - prj::shared_ptr alloc) { + prj::shared_ptr alloc) { bone_dst->id = bone_src->id; bone_dst->parent = bone_src->parent; bone_dst->inv_bind_pose_mat = bone_src->inv_bind_pose_mat; @@ -897,7 +899,7 @@ static void obj_move_data_skin_bone(obj_skin_bone* bone_dst, const obj_skin_bone } static obj_skin_ex_data* obj_move_data_skin_ex_data(const obj_skin_ex_data* ex_src, - prj::shared_ptr alloc) { + prj::shared_ptr alloc) { obj_skin_ex_data* ex_dst = alloc->allocate(); uint32_t num_osage_node = ex_src->num_osage_node; @@ -963,7 +965,7 @@ static obj_skin_ex_data* obj_move_data_skin_ex_data(const obj_skin_ex_data* ex_s } static obj_skin_block_cloth* obj_move_data_skin_block_cloth(const obj_skin_block_cloth* cls_src, - prj::shared_ptr alloc) { + prj::shared_ptr alloc) { obj_skin_block_cloth* cls_dst = alloc->allocate(); cls_dst->mesh_name = obj_move_data_string(cls_src->mesh_name, alloc); cls_dst->backface_mesh_name = obj_move_data_string(cls_src->backface_mesh_name, alloc); @@ -1007,7 +1009,7 @@ static obj_skin_block_cloth* obj_move_data_skin_block_cloth(const obj_skin_block static void obj_move_data_skin_block_cloth_root(obj_skin_block_cloth_root* cloth_root_dst, const obj_skin_block_cloth_root* cloth_root_src, - prj::shared_ptr alloc) { + prj::shared_ptr alloc) { cloth_root_dst->trans = cloth_root_src->trans; cloth_root_dst->normal = cloth_root_src->normal; cloth_root_dst->field_18 = cloth_root_src->field_18; @@ -1022,7 +1024,7 @@ static void obj_move_data_skin_block_cloth_root(obj_skin_block_cloth_root* cloth static void obj_move_data_skin_block_cloth_root_bone_weight(obj_skin_block_cloth_root_bone_weight* bone_weight_dst, const obj_skin_block_cloth_root_bone_weight* bone_weight_src, - prj::shared_ptr alloc) { + prj::shared_ptr alloc) { bone_weight_dst->bone_name = obj_move_data_string(bone_weight_src->bone_name, alloc); bone_weight_dst->weight = bone_weight_src->weight; bone_weight_dst->matrix_index = bone_weight_src->matrix_index; @@ -1030,7 +1032,7 @@ static void obj_move_data_skin_block_cloth_root_bone_weight(obj_skin_block_cloth } static obj_skin_block_constraint* obj_move_data_skin_block_constraint(const obj_skin_block_constraint* cns_src, - prj::shared_ptr alloc) { + prj::shared_ptr alloc) { obj_skin_block_constraint* cns_dst = alloc->allocate(); obj_move_data_skin_block_node(&cns_dst->node, &cns_src->node, alloc); cns_dst->name_index = cns_src->name_index; @@ -1093,7 +1095,7 @@ static obj_skin_block_constraint* obj_move_data_skin_block_constraint(const obj_ static void obj_move_data_skin_block_constraint_up_vector(obj_skin_block_constraint_up_vector* up_vector_dst, const obj_skin_block_constraint_up_vector* up_vector_src, - prj::shared_ptr alloc) { + prj::shared_ptr alloc) { up_vector_dst->active = up_vector_src->active; up_vector_dst->roll = up_vector_src->roll; up_vector_dst->affected_axis = up_vector_src->affected_axis; @@ -1102,7 +1104,7 @@ static void obj_move_data_skin_block_constraint_up_vector(obj_skin_block_constra } static obj_skin_block_expression* obj_move_data_skin_block_expression(const obj_skin_block_expression* exp_src, - prj::shared_ptr alloc) { + prj::shared_ptr alloc) { obj_skin_block_expression* exp_dst = alloc->allocate(); obj_move_data_skin_block_node(&exp_dst->node, &exp_src->node, alloc); exp_dst->name_index = exp_src->name_index; @@ -1119,7 +1121,7 @@ static obj_skin_block_expression* obj_move_data_skin_block_expression(const obj_ } static obj_skin_block_motion* obj_move_data_skin_block_motion(const obj_skin_block_motion* mot_src, - prj::shared_ptr alloc) { + prj::shared_ptr alloc) { obj_skin_block_motion* mot_dst = alloc->allocate(); obj_move_data_skin_block_node(&mot_dst->node, &mot_src->node, alloc); @@ -1136,7 +1138,7 @@ static obj_skin_block_motion* obj_move_data_skin_block_motion(const obj_skin_blo } static void obj_move_data_skin_block_node(obj_skin_block_node* node_dst, const obj_skin_block_node* node_src, - prj::shared_ptr alloc) { + prj::shared_ptr alloc) { node_dst->parent_name = obj_move_data_string(node_src->parent_name, alloc); node_dst->position = node_src->position; node_dst->rotation = node_src->rotation; @@ -1144,7 +1146,7 @@ static void obj_move_data_skin_block_node(obj_skin_block_node* node_dst, const o } static obj_skin_block_osage* obj_move_data_skin_block_osage(const obj_skin_block_osage* osg_src, - prj::shared_ptr alloc) { + prj::shared_ptr alloc) { obj_skin_block_osage* osg_dst = alloc->allocate(); obj_move_data_skin_block_node(&osg_dst->node, &osg_src->node, alloc); osg_dst->start_index = osg_src->start_index; @@ -1162,7 +1164,7 @@ static obj_skin_block_osage* obj_move_data_skin_block_osage(const obj_skin_block } static obj_skin_skin_param* obj_move_data_skin_param(const obj_skin_skin_param* skp_src, - prj::shared_ptr alloc) { + prj::shared_ptr alloc) { if (!skp_src) return 0; @@ -1188,7 +1190,7 @@ static obj_skin_skin_param* obj_move_data_skin_param(const obj_skin_skin_param* return skp_dst; } -static void obj_set_classic_read_inner(obj_set* set, prj::shared_ptr& alloc, stream& s) { +static void obj_set_classic_read_inner(obj_set* set, prj::shared_ptr& alloc, stream& s) { uint32_t version = s.read_uint32_t(); if (version != 0x05062500) { set->ready = false; @@ -1385,7 +1387,7 @@ static void obj_set_classic_write_inner(obj_set* set, stream& s) { s.position_pop(); } -static void obj_classic_read_index(obj_sub_mesh* sub_mesh, prj::shared_ptr alloc, stream& s) { +static void obj_classic_read_index(obj_sub_mesh* sub_mesh, prj::shared_ptr alloc, stream& s) { bool tri_strip = sub_mesh->primitive_type == OBJ_PRIMITIVE_TRIANGLE_STRIP; uint32_t num_index = sub_mesh->num_index; uint32_t* index_array = alloc->allocate(num_index); @@ -1430,7 +1432,8 @@ static void obj_classic_write_index(obj_sub_mesh* sub_mesh, stream& s) { s.align_write(0x04); } -static void obj_classic_read_model(obj* obj, prj::shared_ptr alloc, stream& s, int64_t base_offset) { +static void obj_classic_read_model(obj* obj, prj::shared_ptr alloc, + stream& s, int64_t base_offset) { const size_t mesh_size = 0xD8; s.set_position(base_offset, SEEK_SET); @@ -1673,7 +1676,7 @@ static void obj_classic_write_model(obj* obj, stream& s, int64_t base_offset) { } static void obj_classic_read_model_mesh(obj_mesh* mesh, - prj::shared_ptr alloc, stream& s, int64_t base_offset) { + prj::shared_ptr alloc, stream& s, int64_t base_offset) { const size_t sub_mesh_size = 0x5C; obj_mesh_header mh = {}; @@ -1718,7 +1721,7 @@ static void obj_classic_read_model_mesh(obj_mesh* mesh, } static void obj_classic_read_model_sub_mesh(obj_sub_mesh* sub_mesh, - prj::shared_ptr alloc, stream& s, int64_t base_offset) { + prj::shared_ptr alloc, stream& s, int64_t base_offset) { obj_sub_mesh_header smh = {}; sub_mesh->flags = s.read_uint32_t(); sub_mesh->bounding_sphere.center.x = s.read_float_t(); @@ -1752,7 +1755,7 @@ static void obj_classic_read_model_sub_mesh(obj_sub_mesh* sub_mesh, obj_classic_read_index(sub_mesh, alloc, s); } -static obj_skin* obj_classic_read_skin(prj::shared_ptr alloc, stream& s, int64_t base_offset) { +static obj_skin* obj_classic_read_skin(prj::shared_ptr alloc, stream& s, int64_t base_offset) { obj_skin* sk = alloc->allocate(); s.set_position(base_offset, SEEK_SET); @@ -2522,7 +2525,7 @@ static void obj_classic_write_skin(obj_skin* sk, stream& s, int64_t base_offset) s.position_pop(); } -static obj_skin_ex_data* obj_classic_read_skin_ex_data(prj::shared_ptr alloc, +static obj_skin_ex_data* obj_classic_read_skin_ex_data(prj::shared_ptr alloc, stream& s, int64_t base_offset) { obj_skin_ex_data* ex = alloc->allocate(); s.set_position(base_offset, SEEK_SET); @@ -2687,7 +2690,7 @@ static obj_skin_ex_data* obj_classic_read_skin_ex_data(prj::shared_ptr alloc, stream& s, const char** str) { + prj::shared_ptr alloc, stream& s, const char** str) { obj_skin_block_cloth* cls = alloc->allocate(); uint32_t mesh_name_offset = s.read_uint32_t(); uint32_t backface_mesh_name_offset = s.read_uint32_t(); @@ -2907,7 +2910,7 @@ static void obj_classic_write_skin_block_cloth(obj_skin_block_cloth* cls, } static void obj_classic_read_skin_block_cloth_root(obj_skin_block_cloth_root* cloth_root, - prj::shared_ptr alloc, stream& s, const char** str) { + prj::shared_ptr alloc, stream& s, const char** str) { cloth_root->trans.x = s.read_float_t(); cloth_root->trans.y = s.read_float_t(); cloth_root->trans.z = s.read_float_t(); @@ -2943,7 +2946,7 @@ static void obj_classic_write_skin_block_cloth_root(obj_skin_block_cloth_root* c } static void obj_classic_read_skin_block_cloth_root_bone_weight(obj_skin_block_cloth_root_bone_weight* bone_weight, - prj::shared_ptr alloc, stream& s, const char** str) { + prj::shared_ptr alloc, stream& s, const char** str) { int32_t bone_name_offset = s.read_uint32_t(); bone_weight->bone_name = obj_read_utf8_string_null_terminated_offset(alloc, s, bone_name_offset); bone_weight->weight = s.read_float_t(); @@ -2962,7 +2965,7 @@ static void obj_classic_write_skin_block_cloth_root_bone_weight(obj_skin_block_c } static obj_skin_block_constraint* obj_classic_read_skin_block_constraint( - prj::shared_ptr alloc, stream& s, const char** str) { + prj::shared_ptr alloc, stream& s, const char** str) { obj_skin_block_constraint* cns = alloc->allocate(); obj_classic_read_skin_block_node(&cns->node, alloc, s, str); @@ -3132,7 +3135,7 @@ static void obj_classic_write_skin_block_constraint_attach_point(obj_skin_block_ } static void obj_classic_read_skin_block_constraint_up_vector(obj_skin_block_constraint_up_vector* up_vector, - prj::shared_ptr alloc, stream& s, const char** str) { + prj::shared_ptr alloc, stream& s, const char** str) { up_vector->active = !!s.read_int32_t(); up_vector->roll = s.read_float_t(); up_vector->affected_axis.x = s.read_float_t(); @@ -3163,7 +3166,7 @@ static void obj_classic_write_skin_block_constraint_up_vector(obj_skin_block_con } static obj_skin_block_expression* obj_classic_read_skin_block_expression( - prj::shared_ptr alloc, stream& s, const char** str) { + prj::shared_ptr alloc, stream& s, const char** str) { obj_skin_block_expression* exp = alloc->allocate(); obj_classic_read_skin_block_node(&exp->node, alloc, s, str); @@ -3216,7 +3219,7 @@ static void obj_classic_write_skin_block_expression(obj_skin_block_expression* e } static obj_skin_block_motion* obj_classic_read_skin_block_motion( - prj::shared_ptr alloc, stream& s, const char** str) { + prj::shared_ptr alloc, stream& s, const char** str) { obj_skin_block_motion* mot = alloc->allocate(); obj_classic_read_skin_block_node(&mot->node, alloc, s, str); @@ -3320,7 +3323,7 @@ static void obj_classic_write_skin_block_motion(obj_skin_block_motion* mot, } static void obj_classic_read_skin_block_node(obj_skin_block_node* node, - prj::shared_ptr alloc, stream& s, const char** str) { + prj::shared_ptr alloc, stream& s, const char** str) { uint32_t parent_name_offset = s.read_uint32_t(); node->parent_name = obj_read_utf8_string_null_terminated_offset(alloc, s, parent_name_offset); @@ -3353,7 +3356,7 @@ static void obj_classic_write_skin_block_node(obj_skin_block_node* node, } static obj_skin_block_osage* obj_classic_read_skin_block_osage( - prj::shared_ptr alloc, stream& s, const char** str) { + prj::shared_ptr alloc, stream& s, const char** str) { obj_skin_block_osage* osg = alloc->allocate(); obj_classic_read_skin_block_node(&osg->node, alloc, s, str); @@ -3428,7 +3431,7 @@ static void obj_classic_write_skin_block_osage(obj_skin_block_osage* osg, } static obj_skin_skin_param* obj_classic_read_skin_param( - prj::shared_ptr alloc, stream& s, const char** str) { + prj::shared_ptr alloc, stream& s, const char** str) { obj_skin_skin_param* skp = alloc->allocate(); skp->coli = 0; skp->coli_count = 0; @@ -3521,7 +3524,7 @@ static void obj_classic_write_skin_param(obj_skin_skin_param* skp, } static void obj_classic_read_vertex(obj_mesh* mesh, - prj::shared_ptr alloc, stream& s, int64_t* vertex, int64_t base_offset, uint32_t num_vertex, + prj::shared_ptr alloc, stream& s, int64_t* vertex, int64_t base_offset, uint32_t num_vertex, obj_vertex_format_file vertex_format_file) { obj_vertex_format vertex_format = OBJ_VERTEX_NONE; if (vertex_format_file & OBJ_VERTEX_FILE_POSITION) @@ -3650,10 +3653,14 @@ static void obj_classic_read_vertex(obj_mesh* mesh, break; case OBJ_VERTEX_FILE_BONE_INDEX: for (uint32_t j = 0; j < num_vertex; j++) { - vtx[j].bone_index.x = (int32_t)s.read_float_t(); - vtx[j].bone_index.y = (int32_t)s.read_float_t(); - vtx[j].bone_index.z = (int32_t)s.read_float_t(); - vtx[j].bone_index.w = (int32_t)s.read_float_t(); + int32_t bone_index_x = (int32_t)s.read_float_t(); + int32_t bone_index_y = (int32_t)s.read_float_t(); + int32_t bone_index_z = (int32_t)s.read_float_t(); + int32_t bone_index_w = (int32_t)s.read_float_t(); + vtx[j].bone_index.x = (int16_t)(bone_index_x >= 0 ? bone_index_x / 3 : -1); + vtx[j].bone_index.y = (int16_t)(bone_index_y >= 0 ? bone_index_y / 3 : -1); + vtx[j].bone_index.z = (int16_t)(bone_index_z >= 0 ? bone_index_z / 3 : -1); + vtx[j].bone_index.w = (int16_t)(bone_index_w >= 0 ? bone_index_w / 3 : -1); } break; case OBJ_VERTEX_FILE_UNKNOWN: @@ -3868,7 +3875,7 @@ static void obj_classic_write_vertex(obj_mesh* mesh, } } -static void obj_set_modern_read_inner(obj_set* set, prj::shared_ptr& alloc, stream& s) { +static void obj_set_modern_read_inner(obj_set* set, prj::shared_ptr& alloc, stream& s) { f2_struct st; st.read(s); if (st.header.signature != reverse_endianness_uint32_t('MOSD') || !st.data.size()) @@ -4300,7 +4307,7 @@ static void obj_set_modern_write_inner(obj_set* set, stream& s) { st.write(s, true, set->is_x); } -static void obj_modern_read_index(obj_sub_mesh* sub_mesh, prj::shared_ptr alloc, stream& s) { +static void obj_modern_read_index(obj_sub_mesh* sub_mesh, prj::shared_ptr alloc, stream& s) { bool tri_strip = sub_mesh->primitive_type == OBJ_PRIMITIVE_TRIANGLE_STRIP; uint32_t* index_array = alloc->allocate(sub_mesh->num_index); uint32_t num_index = sub_mesh->num_index; @@ -4382,7 +4389,8 @@ static void obj_modern_write_index(obj_sub_mesh* sub_mesh, s.align_write(0x04); } -static void obj_modern_read_model(obj* obj, prj::shared_ptr alloc, stream& s, int64_t base_offset, +static void obj_modern_read_model(obj* obj, prj::shared_ptr alloc, + stream& s, int64_t base_offset, uint32_t header_length, bool is_x, stream* s_oidx, stream* s_ovtx) { const size_t mesh_size = is_x ? 0x130 : 0xD8; @@ -4942,7 +4950,7 @@ static void obj_modern_write_model(obj* obj, stream& s, } static void obj_modern_read_model_mesh(obj_mesh* mesh, - prj::shared_ptr alloc, stream& s, int64_t base_offset, + prj::shared_ptr alloc, stream& s, int64_t base_offset, uint32_t header_length, bool is_x, stream* s_oidx, stream* s_ovtx) { const size_t sub_mesh_size = is_x ? 0x80 : 0x70; @@ -4992,7 +5000,7 @@ static void obj_modern_read_model_mesh(obj_mesh* mesh, } static void obj_modern_read_model_sub_mesh(obj_sub_mesh* sub_mesh, - prj::shared_ptr alloc, stream& s, int64_t base_offset, + prj::shared_ptr alloc, stream& s, int64_t base_offset, uint32_t header_length, bool is_x, stream* s_oidx) { obj_sub_mesh_header smh = {}; sub_mesh->flags = s.read_uint32_t_reverse_endianness(); @@ -5038,7 +5046,7 @@ static void obj_modern_read_model_sub_mesh(obj_sub_mesh* sub_mesh, obj_modern_read_index(sub_mesh, alloc, *s_oidx); } -static obj_skin* obj_modern_read_skin(prj::shared_ptr alloc, +static obj_skin* obj_modern_read_skin(prj::shared_ptr alloc, stream& s, int64_t base_offset, uint32_t header_length, bool is_x) { obj_skin* sk = alloc->allocate(); s.set_position(base_offset, SEEK_SET); @@ -6540,7 +6548,7 @@ static void obj_modern_write_skin(obj_skin* sk, stream& s, oskn->pof = pof; } -static obj_skin_ex_data* obj_modern_read_skin_ex_data(prj::shared_ptr alloc, +static obj_skin_ex_data* obj_modern_read_skin_ex_data(prj::shared_ptr alloc, stream& s, int64_t base_offset, uint32_t header_length, bool is_x) { obj_skin_ex_data* ex = alloc->allocate(); s.set_position(base_offset, SEEK_SET); @@ -6727,7 +6735,7 @@ static obj_skin_ex_data* obj_modern_read_skin_ex_data(prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x) { + prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x) { obj_skin_block_cloth* cls = alloc->allocate(); int64_t mesh_name_offset = s.read_offset(header_length, is_x); int64_t backface_mesh_name_offset = s.read_offset(header_length, is_x); @@ -6981,7 +6989,7 @@ static void obj_modern_write_skin_block_cloth(obj_skin_block_cloth* cls, } static void obj_modern_read_skin_block_cloth_root(obj_skin_block_cloth_root* cloth_root, - prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x) { + prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x) { cloth_root->trans.x = s.read_float_t(); cloth_root->trans.y = s.read_float_t(); cloth_root->trans.z = s.read_float_t(); @@ -7017,7 +7025,7 @@ static void obj_modern_write_skin_block_cloth_root(obj_skin_block_cloth_root* cl } static void obj_modern_read_skin_block_cloth_root_bone_weight(obj_skin_block_cloth_root_bone_weight* bone_weight, - prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x) { + prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x) { int64_t bone_name_offset = s.read_offset(header_length, is_x); bone_weight->bone_name = obj_read_utf8_string_null_terminated_offset(alloc, s, bone_name_offset); bone_weight->weight = s.read_float_t_reverse_endianness(); @@ -7040,7 +7048,7 @@ static void obj_modern_write_skin_block_cloth_root_bone_weight(obj_skin_block_cl } static obj_skin_block_constraint* obj_modern_read_skin_block_constraint( - prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x) { + prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x) { obj_skin_block_constraint* cns = alloc->allocate(); obj_modern_read_skin_block_node(&cns->node, alloc, s, header_length, str, is_x); @@ -7210,7 +7218,7 @@ static void obj_modern_write_skin_block_constraint_attach_point(obj_skin_block_c } static void obj_modern_read_skin_block_constraint_up_vector(obj_skin_block_constraint_up_vector* up_vector, - prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x) { + prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x) { up_vector->active = !!s.read_int32_t_reverse_endianness(); up_vector->roll = s.read_float_t_reverse_endianness(); up_vector->affected_axis.x = s.read_float_t_reverse_endianness(); @@ -7241,7 +7249,7 @@ static void obj_modern_write_skin_block_constraint_up_vector(obj_skin_block_cons } static obj_skin_block_expression* obj_modern_read_skin_block_expression( - prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x) { + prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x) { obj_skin_block_expression* exp = alloc->allocate(); obj_modern_read_skin_block_node(&exp->node, alloc, s, header_length, str, is_x); @@ -7314,7 +7322,7 @@ static void obj_modern_write_skin_block_expression(obj_skin_block_expression* ex } static obj_skin_block_motion* obj_modern_read_skin_block_motion( - prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x) { + prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x) { obj_skin_block_motion* mot = alloc->allocate(); obj_modern_read_skin_block_node(&mot->node, alloc, s, header_length, str, is_x); @@ -7426,7 +7434,7 @@ static void obj_modern_write_skin_block_motion(obj_skin_block_motion* mot, } static void obj_modern_read_skin_block_node(obj_skin_block_node* node, - prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x) { + prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x) { int64_t parent_name = s.read_offset(header_length, is_x); node->parent_name = obj_read_utf8_string_null_terminated_offset(alloc, s, parent_name); @@ -7463,7 +7471,7 @@ static void obj_modern_write_skin_block_node(obj_skin_block_node* node, } static obj_skin_block_osage* obj_modern_read_skin_block_osage( - prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x) { + prj::shared_ptr alloc, stream& s, uint32_t header_length, const char** str, bool is_x) { obj_skin_block_osage* osg = alloc->allocate(); obj_modern_read_skin_block_node(&osg->node, alloc, s, header_length, str, is_x); @@ -7512,7 +7520,7 @@ static int32_t vtx_fmt_map[] = { 20 }; -static void obj_modern_read_vertex(obj_mesh* mesh, prj::shared_ptr alloc, stream& s, +static void obj_modern_read_vertex(obj_mesh* mesh, prj::shared_ptr alloc, stream& s, int64_t* vertex, const uint32_t vertex_format_index, uint32_t num_vertex, uint32_t size_vertex) { int32_t vtx_fmt = vertex_format_index >= 0 && vertex_format_index <= 20 ? vtx_fmt_map[vertex_format_index] : -1; @@ -7659,19 +7667,15 @@ static void obj_modern_read_vertex(obj_mesh* mesh, prj::shared_ptr a bone_weight.w = (float_t)s.read_int16_t_reverse_endianness(); bone_weight = vec4::div_min_max(bone_weight, -32768.0f, 32767.0f); - vec4i bone_index; - bone_index.x = (int32_t)s.read_uint8_t(); - bone_index.y = (int32_t)s.read_uint8_t(); - bone_index.z = (int32_t)s.read_uint8_t(); - bone_index.w = (int32_t)s.read_uint8_t(); - if (bone_index.x == 0xFF) - bone_index.x = -1; - if (bone_index.y == 0xFF) - bone_index.y = -1; - if (bone_index.z == 0xFF) - bone_index.z = -1; - if (bone_index.w == 0xFF) - bone_index.w = -1; + vec4i16 bone_index; + bone_index.x = s.read_uint8_t(); + bone_index.y = s.read_uint8_t(); + bone_index.z = s.read_uint8_t(); + bone_index.w = s.read_uint8_t(); + bone_index.x = bone_index.x != 0xFF ? bone_index.x / 3 : -1; + bone_index.y = bone_index.y != 0xFF ? bone_index.y / 3 : -1; + bone_index.z = bone_index.z != 0xFF ? bone_index.z / 3 : -1; + bone_index.w = bone_index.w != 0xFF ? bone_index.w / 3 : -1; obj_vertex_validate_bone_data(bone_weight, bone_index); @@ -7689,6 +7693,7 @@ static void obj_modern_read_vertex(obj_mesh* mesh, prj::shared_ptr a mesh->vertex_array = vtx; mesh->num_vertex = num_vertex; mesh->vertex_format = vertex_format; + mesh->attrib.m.compressed = 1; } static void obj_modern_write_vertex(obj_mesh* mesh, stream& s, int64_t* vertex, @@ -7816,12 +7821,10 @@ static void obj_modern_write_vertex(obj_mesh* mesh, stream& s, int64_t* vertex, s.write_int16_t_reverse_endianness((int16_t)bone_weight.z); s.write_int16_t_reverse_endianness((int16_t)bone_weight.w); - vec4u8 bone_index; - vec4i_to_vec4u8(vtx[i].bone_index, bone_index); - s.write_uint8_t(bone_index.x); - s.write_uint8_t(bone_index.y); - s.write_uint8_t(bone_index.z); - s.write_uint8_t(bone_index.w); + s.write_uint8_t((uint8_t)vtx[i].bone_index.x); + s.write_uint8_t((uint8_t)vtx[i].bone_index.y); + s.write_uint8_t((uint8_t)vtx[i].bone_index.z); + s.write_uint8_t((uint8_t)vtx[i].bone_index.w); } } @@ -7831,14 +7834,14 @@ static void obj_modern_write_vertex(obj_mesh* mesh, stream& s, int64_t* vertex, } inline static const char* obj_move_data_string(const char* str, - prj::shared_ptr& alloc) { + prj::shared_ptr& alloc) { if (str) return alloc->allocate(str, utf8_length(str) + 1); return 0; } inline static const char* obj_read_utf8_string_null_terminated_offset( - prj::shared_ptr& alloc, stream& s, int64_t offset) { + prj::shared_ptr& alloc, stream& s, int64_t offset) { size_t len = s.read_utf8_string_null_terminated_offset_length(offset); char* str = alloc->allocate(len + 1); s.position_push(offset, SEEK_SET); diff --git a/src/KKdLib/obj.hpp b/src/KKdLib/obj.hpp index bc6659ca..1ba2964e 100644 --- a/src/KKdLib/obj.hpp +++ b/src/KKdLib/obj.hpp @@ -7,7 +7,7 @@ #include "default.hpp" #include "prj/shared_ptr.hpp" -#include "alloc_data.hpp" +#include "prj/stack_allocator.hpp" #include "mat.hpp" #include "vec.hpp" @@ -213,7 +213,7 @@ union obj_material_shader_attrib { obj_material_shader_attrib_member m; uint32_t w; - obj_material_shader_lighting_type get_lighting_type(); + obj_material_shader_lighting_type get_lighting_type() const; }; struct obj_texture_attrib_member { @@ -237,7 +237,7 @@ union obj_texture_attrib { obj_texture_attrib_member m; uint32_t w; - int32_t get_blend(); + int32_t get_blend() const; }; struct obj_texture_shader_attrib_member { @@ -377,7 +377,7 @@ struct obj_vertex_data { vec4 color0; vec4 color1; vec4 bone_weight; - vec4i bone_index; + vec4i16 bone_index; vec4 unknown; obj_vertex_data(); @@ -709,7 +709,7 @@ struct obj_set { obj_set(); - void move_data(obj_set* set_src, prj::shared_ptr alloc); + void move_data(obj_set* set_src, prj::shared_ptr alloc); void pack_file(void** data, size_t* size); - void unpack_file(prj::shared_ptr alloc, const void* data, size_t size, bool modern); + void unpack_file(prj::shared_ptr alloc, const void* data, size_t size, bool modern); }; diff --git a/src/KKdLib/prj/algorithm.hpp b/src/KKdLib/prj/algorithm.hpp new file mode 100644 index 00000000..ec2ff2b0 --- /dev/null +++ b/src/KKdLib/prj/algorithm.hpp @@ -0,0 +1,40 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder + + Taken from MSVC's VC/include/memory +*/ + +#pragma once + +#include "../default.hpp" +#include +#include + +namespace prj { + template + void sort(std::vector& vec) { + std::sort(vec.begin(), vec.end()); + } + + template + void unique(std::vector& vec) { + if (vec.size() <= 1) + return; + + auto begin = vec.begin(); + auto end = vec.end(); + for (auto i = begin, j = begin + 1; i != end && j != end; ) + if (*i == *j) { + std::move(j + 1, end, j); + end--; + } + else { + i++; + j++; + } + + if (vec.size() != end - begin) + vec.resize(end - begin); + } +} diff --git a/src/KKdLib/prj/shared_ptr.cpp b/src/KKdLib/prj/shared_ptr.cpp deleted file mode 100644 index a6d277b5..00000000 --- a/src/KKdLib/prj/shared_ptr.cpp +++ /dev/null @@ -1,6 +0,0 @@ -/* - by korenkonder - GitHub/GitLab: korenkonder -*/ - -#include "shared_ptr.hpp" diff --git a/src/KKdLib/prj/stack_allocator.cpp b/src/KKdLib/prj/stack_allocator.cpp new file mode 100644 index 00000000..75150e02 --- /dev/null +++ b/src/KKdLib/prj/stack_allocator.cpp @@ -0,0 +1,100 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#include "stack_allocator.hpp" + +namespace prj { + void* stack_allocator::allocate(size_t size) { +#if PRJ_STACK_ALLOCATOR_ORIGINAL_CODE + stack_allocator_node* node = (stack_allocator_node*)begin; +#else + stack_allocator_node* node = next; +#endif + size = max_def(size, 1); + size = align_val(size, 8); + +#if PRJ_STACK_ALLOCATOR_ORIGINAL_CODE + bool allocate = !node || (capacity_end - end) < (ssize_t)size; +#else + bool allocate = true; + if (node) { + stack_allocator_node* last_node = 0; + while (node) { + if ((size_t)(node->capacity - node->size) >= size) + last_node = node; + node = node->next; + } + + if (last_node) { + node = last_node; + allocate = false; + } + else + node = next; + } +#endif + + if (allocate) { + size_t data_size = this->size; + size_t _size = size + sizeof(stack_allocator_node); + size_t mults = 0; + while (data_size < _size) { + data_size *= 2; + if (data_size >= _size) + break; + + mults++; + data_size *= 2; + + if (mults >= 16) { + if (data_size < _size) + return 0; + break; + } + } + + stack_allocator_node* new_node = (stack_allocator_node*)malloc(data_size); + if (!new_node) + return 0; + +#if PRJ_STACK_ALLOCATOR_ORIGINAL_CODE + new_node->next = node; + begin = (uint8_t*)new_node; + end = (uint8_t*)new_node + sizeof(stack_allocator_node); + capacity_end = (uint8_t*)new_node + data_size; +#else + new_node->next = node; + new_node->size = 0; + new_node->capacity = data_size - sizeof(stack_allocator_node); + next = new_node; +#endif + node = new_node; + } + +#if PRJ_STACK_ALLOCATOR_ORIGINAL_CODE + uint8_t* data = end; + end += size; +#else + uint8_t* data = node->data + node->size; + node->size += size; +#endif + memset(data, 0, size); + return data; + } + + void stack_allocator::deallocate() { +#if PRJ_STACK_ALLOCATOR_ORIGINAL_CODE + stack_allocator_node* node = (stack_allocator_node*)begin; +#else + stack_allocator_node* node = next; +#endif + while (node) { + stack_allocator_node* next_node = node->next; + free(node); + node = next_node; + } + next = 0; + } +} \ No newline at end of file diff --git a/src/KKdLib/prj/stack_allocator.hpp b/src/KKdLib/prj/stack_allocator.hpp new file mode 100644 index 00000000..7be09f96 --- /dev/null +++ b/src/KKdLib/prj/stack_allocator.hpp @@ -0,0 +1,85 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#pragma once + +#include "../default.hpp" + +#define PRJ_STACK_ALLOCATOR_ORIGINAL_CODE 0 + +namespace prj { + struct stack_allocator_node { + stack_allocator_node * next; +#if !PRJ_STACK_ALLOCATOR_ORIGINAL_CODE + size_t size; + size_t capacity; +#endif +#pragma warning(suppress: 4200) + uint8_t data[]; + }; + + struct stack_allocator { + size_t size; +#if PRJ_STACK_ALLOCATOR_ORIGINAL_CODE + uint8_t* begin; + uint8_t* end; + uint8_t* capacity_end; +#else + stack_allocator_node* next; +#endif + +#if PRJ_STACK_ALLOCATOR_ORIGINAL_CODE + inline stack_allocator() : begin(), end(), capacity_end() { + size = 4000; + } +#else + inline stack_allocator() : next() { + size = 4000; + } +#endif + + inline ~stack_allocator() { + deallocate(); + } + + void* allocate(size_t size); + void deallocate(); + + template + inline T* allocate() { + return new((T*)allocate(sizeof(T))) T; + } + + template + inline T* allocate(size_t size) { + if (!size) + return 0; + + T* arr = (T*)allocate(sizeof(T) * size); + for (size_t i = 0; i < size; i++) + new(&arr[i]) T(); + return arr; + } + + template + inline T* allocate(const T* src) { + if (!src) + return 0; + + return new((T*)allocate(sizeof(T))) T(*src); + } + + template + inline T* allocate(const T* src, size_t size) { + if (!src || !size) + return 0; + + T* dst = (T*)allocate(sizeof(T) * size); + for (size_t i = 0; i < size; i++) + new(&dst[i]) T(src[i]); + return dst; + } + }; +} diff --git a/src/KKdLib/pv_exp.cpp b/src/KKdLib/pv_exp.cpp index 5a9f5ca8..68bdbe9c 100644 --- a/src/KKdLib/pv_exp.cpp +++ b/src/KKdLib/pv_exp.cpp @@ -10,14 +10,14 @@ #include "io/path.hpp" #include "str_utils.hpp" -static void pv_exp_classic_read_inner(pv_exp* exp, prj::shared_ptr& alloc, stream& s); +static void pv_exp_classic_read_inner(pv_exp* exp, prj::shared_ptr& alloc, stream& s); static void pv_exp_classic_write_inner(pv_exp* exp, stream& s); -static void pv_exp_modern_read_inner(pv_exp* exp, prj::shared_ptr& alloc, stream& s, uint32_t header_length); +static void pv_exp_modern_read_inner(pv_exp* exp, prj::shared_ptr& alloc, stream& s, uint32_t header_length); static void pv_exp_modern_write_inner(pv_exp* exp, stream& s); static const char* pv_exp_read_utf8_string_null_terminated_offset( - prj::shared_ptr& alloc, stream& s, int64_t offset); + prj::shared_ptr& alloc, stream& s, int64_t offset); pv_exp_mot::pv_exp_mot() : face_data(), face_cl_data(), name() { @@ -39,7 +39,7 @@ pv_exp::pv_exp() : ready(), modern(), big_endian(), is_x(), motion_data(), motio } -void pv_exp::move_data(pv_exp* exp_src, prj::shared_ptr alloc) { +void pv_exp::move_data(pv_exp* exp_src, prj::shared_ptr alloc) { if (!exp_src->ready) { ready = false; modern = false; @@ -98,7 +98,7 @@ void pv_exp::pack_file(void** data, size_t* size) { s.copy(data, size); } -void pv_exp::unpack_file(prj::shared_ptr alloc, const void* data, size_t size, bool modern) { +void pv_exp::unpack_file(prj::shared_ptr alloc, const void* data, size_t size, bool modern) { if (!data || !size) return; @@ -119,7 +119,7 @@ void pv_exp::unpack_file(prj::shared_ptr alloc, const void* data, si } } -static void pv_exp_classic_read_inner(pv_exp* exp, prj::shared_ptr& alloc, stream& s) { +static void pv_exp_classic_read_inner(pv_exp* exp, prj::shared_ptr& alloc, stream& s) { if (s.read_uint32_t() != 0x64) { exp->ready = false; exp->modern = false; @@ -301,7 +301,7 @@ static void pv_exp_classic_write_inner(pv_exp* exp, stream& s) { s.write_uint32_t((uint32_t)name_offsets_offset); } -static void pv_exp_modern_read_inner(pv_exp* exp, prj::shared_ptr& alloc, stream& s, uint32_t header_length) { +static void pv_exp_modern_read_inner(pv_exp* exp, prj::shared_ptr& alloc, stream& s, uint32_t header_length) { if (s.read_uint32_t() != 0x64) { exp->ready = false; exp->modern = false; @@ -537,7 +537,7 @@ static void pv_exp_modern_write_inner(pv_exp* exp, stream& s) { } inline static const char* pv_exp_read_utf8_string_null_terminated_offset( - prj::shared_ptr& alloc, stream& s, int64_t offset) { + prj::shared_ptr& alloc, stream& s, int64_t offset) { size_t len = s.read_utf8_string_null_terminated_offset_length(offset); char* str = alloc->allocate(len + 1); s.position_push(offset, SEEK_SET); diff --git a/src/KKdLib/pv_exp.hpp b/src/KKdLib/pv_exp.hpp index c858f995..650f03dc 100644 --- a/src/KKdLib/pv_exp.hpp +++ b/src/KKdLib/pv_exp.hpp @@ -9,7 +9,7 @@ #include #include "default.hpp" #include "prj/shared_ptr.hpp" -#include "alloc_data.hpp" +#include "prj/stack_allocator.hpp" struct pv_exp_data { float_t frame; @@ -39,7 +39,7 @@ struct pv_exp { pv_exp(); - void move_data(pv_exp* exp_src, prj::shared_ptr alloc); + void move_data(pv_exp* exp_src, prj::shared_ptr alloc); void pack_file(void** data, size_t* size); - void unpack_file(prj::shared_ptr alloc, const void* data, size_t size, bool modern); + void unpack_file(prj::shared_ptr alloc, const void* data, size_t size, bool modern); }; diff --git a/src/CRE/time.cpp b/src/KKdLib/time.cpp similarity index 100% rename from src/CRE/time.cpp rename to src/KKdLib/time.cpp diff --git a/src/CRE/time.hpp b/src/KKdLib/time.hpp similarity index 87% rename from src/CRE/time.hpp rename to src/KKdLib/time.hpp index 8a4c49b6..70901052 100644 --- a/src/CRE/time.hpp +++ b/src/KKdLib/time.hpp @@ -5,7 +5,7 @@ #pragma once -#include "../KKdLib/default.hpp" +#include "default.hpp" struct time_struct { LARGE_INTEGER timestamp; diff --git a/src/CRE/timer.cpp b/src/KKdLib/timer.cpp similarity index 100% rename from src/CRE/timer.cpp rename to src/KKdLib/timer.cpp diff --git a/src/CRE/timer.hpp b/src/KKdLib/timer.hpp similarity index 95% rename from src/CRE/timer.hpp rename to src/KKdLib/timer.hpp index 89fe445d..b5711110 100644 --- a/src/CRE/timer.hpp +++ b/src/KKdLib/timer.hpp @@ -5,7 +5,7 @@ #pragma once -#include "../KKdLib/default.hpp" +#include "default.hpp" #include "waitable_timer.hpp" #include diff --git a/src/KKdLib/txp.cpp b/src/KKdLib/txp.cpp index 198ec97e..96f52635 100644 --- a/src/KKdLib/txp.cpp +++ b/src/KKdLib/txp.cpp @@ -42,27 +42,27 @@ uint32_t txp::get_size(txp_format format, uint32_t width, uint32_t height) { return size; case TXP_L8A8: return size * 2; - case TXP_DXT1: - case TXP_DXT1a: - case TXP_DXT3: - case TXP_DXT5: - case TXP_ATI1: - case TXP_ATI2: + case TXP_BC1: + case TXP_BC1a: + case TXP_BC2: + case TXP_BC3: + case TXP_BC4: + case TXP_BC5: width = align_val(width, 4); height = align_val(height, 4); size = width * height; switch (format) { - case TXP_DXT1: + case TXP_BC1: return size / 2; - case TXP_DXT1a: + case TXP_BC1a: return size / 2; - case TXP_DXT3: + case TXP_BC2: return size; - case TXP_DXT5: + case TXP_BC3: return size; - case TXP_ATI1: + case TXP_BC4: return size / 2; - case TXP_ATI2: + case TXP_BC5: return size; } break; diff --git a/src/KKdLib/txp.hpp b/src/KKdLib/txp.hpp index cab39721..410294b8 100644 --- a/src/KKdLib/txp.hpp +++ b/src/KKdLib/txp.hpp @@ -16,12 +16,12 @@ enum txp_format { TXP_RGB5 = 3, TXP_RGB5A1 = 4, TXP_RGBA4 = 5, - TXP_DXT1 = 6, - TXP_DXT1a = 7, - TXP_DXT3 = 8, - TXP_DXT5 = 9, - TXP_ATI1 = 10, - TXP_ATI2 = 11, + TXP_BC1 = 6, + TXP_BC1a = 7, + TXP_BC2 = 8, + TXP_BC3 = 9, + TXP_BC4 = 10, + TXP_BC5 = 11, TXP_L8 = 12, TXP_L8A8 = 13, }; diff --git a/src/CRE/waitable_timer.hpp b/src/KKdLib/waitable_timer.hpp similarity index 97% rename from src/CRE/waitable_timer.hpp rename to src/KKdLib/waitable_timer.hpp index be394cf4..354f4489 100644 --- a/src/CRE/waitable_timer.hpp +++ b/src/KKdLib/waitable_timer.hpp @@ -5,7 +5,7 @@ #pragma once -#include "../KKdLib/default.hpp" +#include "default.hpp" struct waitable_timer { HANDLE handle; diff --git a/src/ReDIVA/classes/data_view/draw_task.cpp b/src/ReDIVA/classes/data_view/draw_task.cpp index a53431c3..374c9436 100644 --- a/src/ReDIVA/classes/data_view/draw_task.cpp +++ b/src/ReDIVA/classes/data_view/draw_task.cpp @@ -11,9 +11,15 @@ static const char* draw_object_type_name[] = { "Opaque", + "Opaque [Show Vector]", + "Opaque [Show Vector]", "Translucent", + "Translucent [Show Vector]", + "Translucent [Show Vector]", "Translucent (No Shadow)", "Transparent", + "Transparent [Show Vector]", + "Transparent [Show Vector]", "Shadow Chara", "Shadow Stage", "Type 6", @@ -48,18 +54,18 @@ static const char* draw_object_type_name[] = { "[Local] Translucent (Alpha Order 2)", }; -static const char* draw_task_type_name[] = { - "Object", - "Primitive", +static const char* mdl_obj_kind_name[] = { + "Normal", + "Etc", "Preprocess", - "Object Translucent", + "User", }; -typedef std::pair draw_task_sort; +typedef std::pair mdl_obj_data_sort; struct data_view_draw_task { render_context* rctx; - std::vector draw_tasks; + std::vector obj_data; data_view_draw_task(); ~data_view_draw_task(); @@ -70,7 +76,7 @@ extern int32_t height; const char* data_view_draw_task_window_title = "Draw Task##Data Viewer"; -static void data_view_draw_task_imgui_draw_object(draw_object* object); +static void data_view_draw_task_imgui_sub_mesh_args(mdl::ObjSubMeshArgs* args); bool data_view_draw_task_init(class_data* data, render_context* rctx) { data_view_draw_task* data_view = new data_view_draw_task; @@ -111,8 +117,8 @@ void data_view_draw_task_imgui(class_data* data) { } render_context* rctx = data_view->rctx; - std::vector* draw_task_array = rctx->object_data.draw_task_array; - std::vector& draw_tasks_sort = data_view->draw_tasks; + std::vector* obj = rctx->disp_manager.obj; + std::vector& obj_data = data_view->obj_data; ImGuiTreeNodeFlags tree_node_base_flags = 0; tree_node_base_flags |= ImGuiTreeNodeFlags_OpenOnDoubleClick; @@ -121,12 +127,11 @@ void data_view_draw_task_imgui(class_data* data) { ImGuiTreeNodeFlags tree_node_flags; - for (int32_t i = DRAW_OBJECT_OPAQUE; i < DRAW_OBJECT_MAX; i++) { - bool translucent_no_shadow = i == DRAW_OBJECT_TRANSLUCENT_NO_SHADOW; + for (int32_t i = mdl::OBJ_TYPE_OPAQUE; i < mdl::OBJ_TYPE_MAX; i++) { tree_node_flags = tree_node_base_flags; ImGui::PushID(i); - std::vector& draw_tasks = draw_task_array[i]; + std::vector& draw_tasks = obj[i]; size_t count = draw_tasks.size(); bool enable = count > 0; ImGui::DisableElementPush(enable); @@ -137,61 +142,57 @@ void data_view_draw_task_imgui(class_data* data) { continue; } - draw_tasks_sort.reserve(count); - for (draw_task*& j : draw_tasks) { - draw_task_sort draw_task; - draw_task.first = j; - draw_task.second = hash_murmurhash(&j, 8, 0, true); - draw_tasks_sort.push_back(draw_task); - } + obj_data.reserve(count); + for (mdl::ObjData*& j : draw_tasks) + obj_data.push_back({ j, hash_murmurhash(&j, 8, 0, true) }); ImGui::Text(" Hash; Type"); - for (draw_task_sort& j : draw_tasks_sort) { - draw_task* task = j.first; - if (task->type < DRAW_TASK_OBJECT || task->type > DRAW_TASK_OBJECT_TRANSLUCENT) + for (mdl_obj_data_sort& j : obj_data) { + mdl::ObjData* data = j.first; + if (data->kind < mdl::OBJ_KIND_NORMAL || data->kind > mdl::OBJ_KIND_TRANSLUCENT) continue; - ImGui::PushID(task); + ImGui::PushID(data); if (!ImGui::TreeNodeEx("", tree_node_flags, "%08x; %s", - j.second, draw_task_type_name[task->type])) { + j.second, mdl_obj_kind_name[data->kind])) { ImGui::PopID(); continue; } + mat4& mat = data->mat; + vec3 trans; - mat4_get_translation(&task->mat, &trans); - vec3 rot; - mat4_get_rotation(&task->mat, &rot); - rot *= RAD_TO_DEG_FLOAT; - vec3 scale; - mat4_get_scale(&task->mat, &scale); + mat4_get_translation(&mat, &trans); + mat4_get_rotation(&mat, &rot); + mat4_get_scale(&mat, &scale); + rot *= RAD_TO_DEG_FLOAT; ImGui::Text("Trans: %9.4f %9.4f %9.4f", trans.x, trans.y, trans.z); ImGui::Text(" Rot: %9.4f %9.4f %9.4f", rot.x, rot.y, rot.z); ImGui::Text("Scale: %9.4f %9.4f %9.4f", scale.x, scale.y, scale.z); - switch (task->type) { - case DRAW_TASK_OBJECT: { + switch (data->kind) { + case mdl::OBJ_KIND_NORMAL: { if (ImGui::TreeNodeEx("Data", ImGuiTreeNodeFlags_DefaultOpen)) { - data_view_draw_task_imgui_draw_object(&task->data.object); + data_view_draw_task_imgui_sub_mesh_args(&data->args.sub_mesh); ImGui::TreePop(); } } break; - case DRAW_TASK_OBJECT_PRIMITIVE: { + case mdl::OBJ_KIND_ETC: { } break; - case DRAW_TASK_OBJECT_PREPROCESS: { + case mdl::OBJ_KIND_USER: { } break; - case DRAW_TASK_OBJECT_TRANSLUCENT: { - draw_task_object_translucent* object_translucent = &task->data.object_translucent; - for (uint32_t l = 0; l < 40 && l < object_translucent->count; l++) { + case mdl::OBJ_KIND_TRANSLUCENT: { + mdl::ObjTranslucentArgs* translucent = &data->args.translucent; + for (uint32_t l = 0; l < 40 && l < translucent->count; l++) { ImGui::PushID(l); if (ImGui::TreeNodeEx("", ImGuiTreeNodeFlags_DefaultOpen, "Data %2d", l)) { - data_view_draw_task_imgui_draw_object(object_translucent->objects[l]); + data_view_draw_task_imgui_sub_mesh_args(translucent->sub_mesh[l]); ImGui::TreePop(); } ImGui::PopID(); @@ -202,7 +203,7 @@ void data_view_draw_task_imgui(class_data* data) { ImGui::TreePop(); ImGui::PopID(); } - draw_tasks_sort.clear(); + obj_data.clear(); ImGui::TreePop(); ImGui::DisableElementPop(enable); @@ -230,7 +231,7 @@ data_view_draw_task::~data_view_draw_task() { } -static void data_view_draw_task_imgui_draw_object(draw_object* object) { - ImGui::Text("Mesh Name: %s", object->mesh->name); - ImGui::Text("Sub Mesh Index: %lld", object->sub_mesh - object->mesh->submesh_array); +static void data_view_draw_task_imgui_sub_mesh_args(mdl::ObjSubMeshArgs* args) { + ImGui::Text("Mesh Name: %s", args->mesh->name); + ImGui::Text("Sub Mesh Index: %lld", args->sub_mesh - args->mesh->submesh_array); } \ No newline at end of file diff --git a/src/ReDIVA/classes/glitter_editor.cpp b/src/ReDIVA/classes/glitter_editor.cpp index 8cf6a54a..38d6d7c1 100644 --- a/src/ReDIVA/classes/glitter_editor.cpp +++ b/src/ReDIVA/classes/glitter_editor.cpp @@ -17,12 +17,11 @@ #include "../../CRE/Glitter/glitter.hpp" #include "../../CRE/camera.hpp" #include "../../CRE/data.hpp" -#include "../../CRE/draw_task.hpp" #include "../../CRE/gl_state.hpp" #include "../../CRE/render_context.hpp" -#include "../../CRE/shader_glsl.hpp" #include "../../CRE/stage.hpp" #include "../../CRE/static_var.hpp" +#include "../data_test/stage_test.hpp" #include "../imgui_helper.hpp" #include "../input.hpp" #include @@ -232,7 +231,7 @@ static void glitter_editor_property_emitter(GlitterEditor* glt_edt, class_data* static void glitter_editor_property_particle(GlitterEditor* glt_edt, class_data* data); static bool glitter_editor_property_particle_texture(GlitterEditor* glt_edt, class_data* data, const char* label, char** items, Glitter::Particle* particle, - int32_t* tex, uint64_t* tex_hash, int32_t tex_idx, bool* tex_anim, + GLuint* tex, uint64_t* tex_hash, int32_t tex_idx, bool* tex_anim, int32_t* tex_frame, int32_t* tex_index, int32_t* tex_tex); static void glitter_editor_popups(GlitterEditor* glt_edt, class_data* data); @@ -2164,22 +2163,22 @@ static void glitter_editor_test_window(GlitterEditor* glt_edt, class_data* data) ImGui::TableNextColumn(); w = ImGui::GetContentRegionAvailWidth(); - if (ImGui::ButtonEnterKeyPressed("Play (T)", { w, 0.0f }) || ImGui::IsKeyPressed(GLFW_KEY_T)) + if (ImGui::ButtonEnterKeyPressed("Play (T)", { w, 0.0f }) || ImGui::IsKeyPressed(ImGuiKey_T)) glt_edt->input_play = true; ImGui::TableNextColumn(); w = ImGui::GetContentRegionAvailWidth(); - if (ImGui::ButtonEnterKeyPressed("Reload (Y)", { w, 0.0f }) || ImGui::IsKeyPressed(GLFW_KEY_Y)) + if (ImGui::ButtonEnterKeyPressed("Reload (Y)", { w, 0.0f }) || ImGui::IsKeyPressed(ImGuiKey_Y)) glt_edt->input_reload = true; ImGui::TableNextColumn(); w = ImGui::GetContentRegionAvailWidth(); - if (ImGui::ButtonEnterKeyPressed("Pause (F)", { w, 0.0f }) || ImGui::IsKeyPressed(GLFW_KEY_F)) + if (ImGui::ButtonEnterKeyPressed("Pause (F)", { w, 0.0f }) || ImGui::IsKeyPressed(ImGuiKey_F)) glt_edt->input_pause = true; ImGui::TableNextColumn(); w = ImGui::GetContentRegionAvailWidth(); - if (ImGui::ButtonEnterKeyPressed("Reset (G)", { w, 0.0f }) || ImGui::IsKeyPressed(GLFW_KEY_G)) + if (ImGui::ButtonEnterKeyPressed("Reset (G)", { w, 0.0f }) || ImGui::IsKeyPressed(ImGuiKey_G)) glt_edt->input_reset = true; ImGui::EndTable(); } @@ -2821,22 +2820,22 @@ static void glitter_editor_play_manager(GlitterEditor* glt_edt) { ImGui::TableNextColumn(); w = ImGui::GetContentRegionAvailWidth(); - if (ImGui::ButtonEnterKeyPressed("Play (T)", { w, 0.0f }) || ImGui::IsKeyPressed(GLFW_KEY_T)) + if (ImGui::ButtonEnterKeyPressed("Play (T)", { w, 0.0f }) || ImGui::IsKeyPressed(ImGuiKey_T)) glt_edt->input_play = true; ImGui::TableNextColumn(); w = ImGui::GetContentRegionAvailWidth(); - if (ImGui::ButtonEnterKeyPressed("Reload (Y)", { w, 0.0f }) || ImGui::IsKeyPressed(GLFW_KEY_Y)) + if (ImGui::ButtonEnterKeyPressed("Reload (Y)", { w, 0.0f }) || ImGui::IsKeyPressed(ImGuiKey_Y)) glt_edt->input_reload = true; ImGui::TableNextColumn(); w = ImGui::GetContentRegionAvailWidth(); - if (ImGui::ButtonEnterKeyPressed("Pause (F)", { w, 0.0f }) || ImGui::IsKeyPressed(GLFW_KEY_F)) + if (ImGui::ButtonEnterKeyPressed("Pause (F)", { w, 0.0f }) || ImGui::IsKeyPressed(ImGuiKey_F)) glt_edt->input_pause = true; ImGui::TableNextColumn(); w = ImGui::GetContentRegionAvailWidth(); - if (ImGui::ButtonEnterKeyPressed("Reset (G)", { w, 0.0f }) || ImGui::IsKeyPressed(GLFW_KEY_G)) + if (ImGui::ButtonEnterKeyPressed("Reset (G)", { w, 0.0f }) || ImGui::IsKeyPressed(ImGuiKey_G)) glt_edt->input_reset = true; ImGui::EndTable(); } @@ -4196,7 +4195,7 @@ static void glitter_editor_property_particle(GlitterEditor* glt_edt, class_data* ImGui::PushID(-1); if (ImGui::Selectable("None", !handler) - || ImGui::ItemKeyPressed(GLFW_KEY_ENTER, true)) + || ImGui::ItemKeyPressed(ImGuiKey_Enter)) set_index = -1; ImGui::PopID(); @@ -4207,7 +4206,7 @@ static void glitter_editor_property_particle(GlitterEditor* glt_edt, class_data* ImGui::PushID((int32_t)i); if (ImGui::Selectable(handler->name.c_str(), handler->set_id == set_id) - || ImGui::ItemKeyPressed(GLFW_KEY_ENTER, true) + || ImGui::ItemKeyPressed(ImGuiKey_Enter) || (ImGui::IsItemFocused() && handler->set_id != set_id)) set_index = i; ImGui::PopID(); @@ -4253,7 +4252,7 @@ static void glitter_editor_property_particle(GlitterEditor* glt_edt, class_data* ImGui::PushID(-1); if (ImGui::Selectable("None", !obj) - || ImGui::ItemKeyPressed(GLFW_KEY_ENTER, true) + || ImGui::ItemKeyPressed(ImGuiKey_Enter) || (ImGui::IsItemFocused() && obj)) obj_index = -1; ImGui::PopID(); @@ -4262,7 +4261,7 @@ static void glitter_editor_property_particle(GlitterEditor* glt_edt, class_data* ImGui::PushID(i); ::obj* obj = &set->obj_data[i]; if (ImGui::Selectable(obj->name, obj->id == obj_id) - || ImGui::ItemKeyPressed(GLFW_KEY_ENTER, true) + || ImGui::ItemKeyPressed(ImGuiKey_Enter) || (ImGui::IsItemFocused() && obj->id != obj_id)) obj_index = i; ImGui::PopID(); @@ -4312,7 +4311,7 @@ static void glitter_editor_property_particle(GlitterEditor* glt_edt, class_data* static bool glitter_editor_property_particle_texture(GlitterEditor* glt_edt, class_data* data, const char* label, char** items, Glitter::Particle* particle, - int32_t* tex, uint64_t* tex_hash, int32_t tex_idx, bool* tex_anim, + GLuint* tex, uint64_t* tex_hash, int32_t tex_idx, bool* tex_anim, int32_t* tex_frame, int32_t* tex_index, int32_t* tex_tex) { Glitter::EffectGroup* eg = glt_edt->effect_group; size_t rc = eg->resources_count; @@ -4395,7 +4394,7 @@ static bool glitter_editor_property_particle_texture(GlitterEditor* glt_edt, for (int32_t n = 0; n <= rc; n++) { ImGui::PushID(n); if (ImGui::Selectable(items[n], tex_idx == n) - || ImGui::ItemKeyPressed(GLFW_KEY_ENTER, true) + || ImGui::ItemKeyPressed(ImGuiKey_Enter) || (ImGui::IsItemFocused() && tex_idx != n)) tex_idx = n; @@ -5818,13 +5817,13 @@ static void glitter_editor_curve_editor_window(GlitterEditor* glt_edt) { glitter_editor_curve_editor_key_manager(glt_edt, keys, &add_key, &del_key); if (ImGui::IsWindowFocused()) { - if (add_key && ImGui::IsKeyPressed(GLFW_KEY_INSERT)) + if (add_key && ImGui::IsKeyPressed(ImGuiKey_Insert)) crv_edt->add_key = true; - else if (del_key && ImGui::IsKeyPressed(GLFW_KEY_DELETE)) + else if (del_key && ImGui::IsKeyPressed(ImGuiKey_Delete)) crv_edt->del_key = true; - else if (ImGui::IsKeyPressed(GLFW_KEY_RIGHT)) + else if (ImGui::IsKeyPressed(ImGuiKey_RightArrow)) crv_edt->frame++; - else if (ImGui::IsKeyPressed(GLFW_KEY_LEFT)) + else if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow)) crv_edt->frame--; if (curve) @@ -6032,7 +6031,7 @@ static void glitter_editor_curve_editor_window(GlitterEditor* glt_edt) { if (hovered && ImGui::IsMouseClicked(ImGuiMouseButton_Left, false)) can_drag = true; - if (can_drag && (ImGui::IsKeyDown(GLFW_KEY_LEFT_SHIFT) || ImGui::IsKeyDown(GLFW_KEY_RIGHT_SHIFT))) { + if (can_drag && (ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift))) { crv_edt->key = glitter_editor_curve_editor_get_closest_key(glt_edt, curve); crv_edt->frame = crv_edt->key->frame; } @@ -6471,8 +6470,8 @@ static void glitter_editor_curve_editor_window(GlitterEditor* glt_edt) { static int32_t base_frame; static float_t base_y; - if ((ImGui::IsKeyPressed(GLFW_KEY_LEFT_SHIFT, false) && !ImGui::IsKeyDown(GLFW_KEY_RIGHT_SHIFT)) - || (ImGui::IsKeyPressed(GLFW_KEY_RIGHT_SHIFT, false) && !ImGui::IsKeyDown(GLFW_KEY_LEFT_SHIFT))) { + if ((ImGui::IsKeyPressed(ImGuiKey_LeftShift, false) && !ImGui::IsKeyDown(ImGuiKey_RightShift)) + || (ImGui::IsKeyPressed(ImGuiKey_RightShift, false) && !ImGui::IsKeyDown(ImGuiKey_LeftShift))) { base_frame = i->frame; base_y = base_value; } @@ -6489,15 +6488,15 @@ static void glitter_editor_curve_editor_window(GlitterEditor* glt_edt) { } static float_t mouse_delta_history; - if ((ImGui::IsKeyPressed(GLFW_KEY_LEFT_SHIFT, false) && !ImGui::IsKeyDown(GLFW_KEY_RIGHT_SHIFT)) - || (ImGui::IsKeyPressed(GLFW_KEY_RIGHT_SHIFT, false) && !ImGui::IsKeyDown(GLFW_KEY_LEFT_SHIFT))) + if ((ImGui::IsKeyPressed(ImGuiKey_LeftShift, false) && !ImGui::IsKeyDown(ImGuiKey_RightShift)) + || (ImGui::IsKeyPressed(ImGuiKey_RightShift, false) && !ImGui::IsKeyDown(ImGuiKey_LeftShift))) mouse_delta_history = io.MouseDelta.y; - else if ((ImGui::IsKeyReleased(GLFW_KEY_LEFT_SHIFT) && !ImGui::IsKeyDown(GLFW_KEY_RIGHT_SHIFT)) - || ImGui::IsKeyReleased(GLFW_KEY_RIGHT_SHIFT) && !ImGui::IsKeyDown(GLFW_KEY_LEFT_SHIFT)) { + else if ((ImGui::IsKeyReleased(ImGuiKey_LeftShift) && !ImGui::IsKeyDown(ImGuiKey_RightShift)) + || ImGui::IsKeyReleased(ImGuiKey_RightShift) && !ImGui::IsKeyDown(ImGuiKey_LeftShift)) { y += mouse_delta_history; y += io.MouseDelta.y; } - else if (ImGui::IsKeyDown(GLFW_KEY_LEFT_SHIFT) || ImGui::IsKeyDown(GLFW_KEY_RIGHT_SHIFT)) + else if (ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift)) mouse_delta_history += io.MouseDelta.y; else y += io.MouseDelta.y; @@ -6595,7 +6594,7 @@ static void glitter_editor_curve_editor_window(GlitterEditor* glt_edt) { if (!crv_edt->key_edit && can_drag && ImGui::IsMouseDown(ImGuiMouseButton_Left) && (!holding_tan || dragged)) { int32_t frame = (int32_t)roundf((io.MousePos.x - canvas_pos.x) / frame_width); crv_edt->frame = clamp_def(frame + start_time, start_time, end_time); - if (ImGui::IsKeyDown(GLFW_KEY_LEFT_SHIFT) || ImGui::IsKeyDown(GLFW_KEY_RIGHT_SHIFT)) { + if (ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift)) { crv_edt->key = glitter_editor_curve_editor_get_closest_key(glt_edt, curve); crv_edt->frame = crv_edt->key->frame; } @@ -6622,7 +6621,7 @@ static void glitter_editor_curve_editor_window(GlitterEditor* glt_edt) { draw_list->PopClipRect(); if (hovered) - if (ImGui::IsKeyDown(GLFW_KEY_LEFT_SHIFT) || ImGui::IsKeyDown(GLFW_KEY_RIGHT_SHIFT)) + if (ImGui::IsKeyDown(ImGuiKey_LeftShift) || ImGui::IsKeyDown(ImGuiKey_RightShift)) crv_edt->timeline_pos -= io.MouseWheel * 25.0f * 4.0f; else crv_edt->timeline_pos -= io.MouseWheel * 25.0f; diff --git a/src/ReDIVA/classes/graphics/light.cpp b/src/ReDIVA/classes/graphics/light.cpp index 4f83a17b..01862572 100644 --- a/src/ReDIVA/classes/graphics/light.cpp +++ b/src/ReDIVA/classes/graphics/light.cpp @@ -110,7 +110,7 @@ void graphics_light_imgui(class_data* data) { ImGui::PushID(&i); if (ImGui::Selectable(i.name.c_str(), _stage_index == stage_idx) - || ImGui::ItemKeyPressed(GLFW_KEY_ENTER, true) + || ImGui::ItemKeyPressed(ImGuiKey_Enter) || (ImGui::IsItemFocused() && _stage_index != stage_idx)) { stage_index = -1; _stage_index = stage_idx; diff --git a/src/ReDIVA/classes/graphics/post_process.cpp b/src/ReDIVA/classes/graphics/post_process.cpp index 473852d1..f37054a2 100644 --- a/src/ReDIVA/classes/graphics/post_process.cpp +++ b/src/ReDIVA/classes/graphics/post_process.cpp @@ -231,6 +231,8 @@ void graphics_post_process_imgui(class_data* data) { if (ImGui::ButtonEnterKeyPressed("Reset Post Process", { 0, 0 })) { stage* stg = task_stage_get_current_stage(); light_param_data_storage_data_set_stage(stg->index); + dof->set_dof_debug(); + dof->set_dof_pv(); } data->imgui_focus |= ImGui::IsWindowFocused(); diff --git a/src/ReDIVA/classes/graphics/render_settings.cpp b/src/ReDIVA/classes/graphics/render_settings.cpp index d127e20e..77fbb1d4 100644 --- a/src/ReDIVA/classes/graphics/render_settings.cpp +++ b/src/ReDIVA/classes/graphics/render_settings.cpp @@ -6,7 +6,7 @@ #include "render_settings.hpp" #include "../../../CRE/lock.hpp" #include "../../../CRE/static_var.hpp" -#include "../../../CRE/timer.hpp" +#include "../../../KKdLib/timer.hpp" #include "../../imgui_helper.hpp" #include "../../render.hpp" diff --git a/src/ReDIVA/data_edit.cpp b/src/ReDIVA/data_edit.cpp index 69de4cee..50aaa488 100644 --- a/src/ReDIVA/data_edit.cpp +++ b/src/ReDIVA/data_edit.cpp @@ -1,8 +1,6 @@ /* by korenkonder GitHub/GitLab: korenkonder - - Some code is from LearnOpenGL */ #if defined(ReDIVA_DEV) @@ -367,7 +365,7 @@ DataEdit::~DataEdit() { bool DataEdit::Init() { Reset(); - task_rob_manager_append_task(); + task_rob_manager_add_task(); return true; } @@ -491,7 +489,7 @@ bool DataEdit::Ctrl() { bool DataEdit::Dest() { rob_chara_array_free_chara_id(chara_id); - task_rob_manager_free_task(); + task_rob_manager_del_task(); Reset(); return true; } diff --git a/src/ReDIVA/data_initialize.cpp b/src/ReDIVA/data_initialize.cpp index 404d7afa..df391eff 100644 --- a/src/ReDIVA/data_initialize.cpp +++ b/src/ReDIVA/data_initialize.cpp @@ -5,6 +5,8 @@ #include "data_initialize.hpp" #include "../CRE/data.hpp" +#include "../CRE/hand_item.hpp" +#include "../CRE/item_table.hpp" #include "../CRE/object.hpp" #include "../CRE/sound.hpp" #include "game_state.hpp" @@ -89,7 +91,7 @@ bool TaskDataInit::Ctrl() { case 8: object_storage_load_set(aft_data, aft_obj_db, dbg_set_id); //bone_database_read(); - //item_table_handler_array_read(); + item_table_handler_array_read(); sound_work_read_farc("rom/sound/se.farc"); sound_work_read_farc("rom/sound/button.farc"); sound_work_read_farc("rom/sound/se_cmn.farc"); @@ -116,7 +118,7 @@ bool TaskDataInit::Ctrl() { //sub_1403847B0(); //sub_1403882D0(); //sub_140388560(); - //hand_item_data_read_file(); + hand_item_handler_data_read(); //rob_sleeve_data_read_file(); //sub_140542F80(); state = 9; @@ -124,7 +126,7 @@ bool TaskDataInit::Ctrl() { case 9: if (!object_storage_load_obj_set_check_not_read(dbg_set_id) //&& !bone_database_load() - //&& !item_table_handler_array_load() + && !item_table_handler_array_load() && !sound_work_load_farc("rom/sound/se.farc") && !sound_work_load_farc("rom/sound/button.farc") && !sound_work_load_farc("rom/sound/se_cmn.farc") @@ -142,7 +144,7 @@ bool TaskDataInit::Ctrl() { //&& !sub_14038F630() //&& !sub_140385220() //&& !sub_140388A50() - //&& !hand_item_data_parse_file() + && !hand_item_handler_data_load() //&& !rob_sleeve_data_parse_file() ) { //sub_14037F680(); @@ -178,14 +180,14 @@ void TaskDataInit::Disp() { } -bool task_data_init_append_task() { - return app::TaskWork::AppendTask(&task_data_init, "DATA_INITIALIZE"); +bool task_data_init_add_task() { + return app::TaskWork::AddTask(&task_data_init, "DATA_INITIALIZE"); } bool task_data_init_check_state() { return task_data_init.state == 12; } -bool task_data_init_free_task() { - return task_data_init.SetDest(); +bool task_data_init_del_task() { + return task_data_init.DelTask(); } diff --git a/src/ReDIVA/data_initialize.hpp b/src/ReDIVA/data_initialize.hpp index 63440102..efaf8fc7 100644 --- a/src/ReDIVA/data_initialize.hpp +++ b/src/ReDIVA/data_initialize.hpp @@ -24,6 +24,6 @@ public: virtual void Disp() override; }; -extern bool task_data_init_append_task(); +extern bool task_data_init_add_task(); extern bool task_data_init_check_state(); -extern bool task_data_init_free_task(); +extern bool task_data_init_del_task(); diff --git a/src/ReDIVA/data_test/auth_3d_test.cpp b/src/ReDIVA/data_test/auth_3d_test.cpp index 035751ff..b4d5e715 100644 --- a/src/ReDIVA/data_test/auth_3d_test.cpp +++ b/src/ReDIVA/data_test/auth_3d_test.cpp @@ -16,14 +16,276 @@ #include "../imgui_helper.hpp" #include "../input.hpp" +static int auth_3d_test_window_uid_quicksort_compare_func(void const* src1, void const* src2); + extern int32_t width; extern int32_t height; extern float_t frame_speed; extern render_context* rctx_ptr; +Auth3dTestTask* auth_3d_test_task; Auth3dTestWindow* auth_3d_test_window; -static int auth_3d_test_window_uid_quicksort_compare_func(void const* src1, void const* src2); +Auth3dTestTask::Auth3dTestTask::Window::Window() { + stage_link_change = true; + obj_link = true; +} + +Auth3dTestTask::Auth3dTestTask() { + field_1C0 = 10; + field_1C4 = 10; + field_1C8 = 10; + field_1CC = 10; + field_1D0 = false; + field_1D4 = 0; + auth_3d_id = {}; + auth_3d_uid = -1; + repeat = true; + left_right_reverse = false; + field_1E3 = false; + field_1E4 = 0; + field_1E8 = false; + field_1E9 = false; + black_mask_listener = false; + field_1EB = true; + field_1EC = 1; + effcmn_obj_set = -1; + field_210 = 1; + field_328 = false; + field_329 = true; + field_32A = true; + stage_index = -1; + load_stage_index = 0; + trans_value = 0.0f; + rot_y_value = 0.0f; + field_388 = false; + field_38C = 0; + field_390 = false; + field_394 = 0; +} + +Auth3dTestTask:: ~Auth3dTestTask() { + +} + +bool Auth3dTestTask::Init() { + data_struct* aft_data = &data_list[DATA_AFT]; + object_database* aft_obj_db = &aft_data->data_ft.obj_db; + + field_1D0 = false; + field_1D4 = 0; + auth_3d_id = {}; + auth_3d_uid = -1; + repeat = true; + left_right_reverse = false; + field_1E3 = false; + field_1E4 = 0; + field_1E8 = false; + field_1E9 = false; + black_mask_listener = false; + field_1EC = 1; + effcmn_obj_set = aft_obj_db->get_object_set_id("EFFCMN"); + field_210 = 1; + field_328 = false; + field_329 = true; + field_32A = true; + stage_index = -1; + load_stage_index = 0; + trans_value = 0.0f; + rot_y_value = 0.0f; + field_390 = true; + field_394 = 0; + category.clear(); + category.shrink_to_fit(); + load_category.clear(); + load_category.shrink_to_fit(); + obj_sets.clear(); + obj_sets.shrink_to_fit(); + + rctx_ptr->render_manager.shadow_ptr->self_shadow = true; + clear_color = { 0x99, 0x99, 0x99, 0x00 }; + task_stage_load_task("A3D_STAGE"); + object_storage_load_set(aft_data, aft_obj_db, effcmn_obj_set); + return true; +} + +bool Auth3dTestTask::Ctrl() { + SetStage(); + SetAuth3dId(); + + if (field_1EC == 1 && !object_storage_load_obj_set_check_not_read(effcmn_obj_set)) + field_1EC = 4; + + if (field_1D4 == 1 && auth_3d_id.check_loaded()) + field_1D4 = 2; + + if (field_1D4 == 2) { + auth_3d_id.set_enable(true); + auth_3d_id.set_paused(false); + auth_3d_id.set_repeat(repeat); + auth_3d_id.set_left_right_reverse(left_right_reverse); + field_1D4 = 4; + } + + if (auth_3d_id.check_not_empty()) { + mat4 mat; + mat4_translate(&trans_value, &mat); + mat4_rotate_y_mult(&mat, rot_y_value * DEG_TO_RAD_FLOAT, &mat); + auth_3d_id.set_mat(mat); + } + return false; +} + +bool Auth3dTestTask::Dest() { + auth_3d_id.unload_id(rctx_ptr); + object_storage_unload_set(effcmn_obj_set); + task_stage_unload_task(); + clear_color = color_black; + rctx_ptr->render_manager.shadow_ptr->self_shadow = true; + if (category.size()) + auth_3d_data_unload_category(category.c_str()); + category.clear(); + category.shrink_to_fit(); + load_category.clear(); + load_category.shrink_to_fit(); + for (uint32_t& i : obj_sets) + object_storage_unload_set(i); + obj_sets.clear(); + obj_sets.shrink_to_fit(); + return true; +} + +void Auth3dTestTask::DispAuth3dChara(::auth_3d_id& id) { + id.get_uid(); // ??? + rob_chara_item_equip* rob_itm_equip = rob_chara_array_get_item_equip(0); + if (!rob_itm_equip) + return; + + for (int32_t i = ITEM_KAMI; i < ITEM_MAX; i++) + rob_itm_equip->set_disp((item_id)i, true); +} + +void Auth3dTestTask::DispChara() { + if (auth_3d_id.check_not_empty()) + Auth3dTestTask::DispAuth3dChara(auth_3d_id); +} + +void Auth3dTestTask::SetAuth3dId() { + if (task_stage_check_not_loaded() || auth_3d_uid == -1 + || !load_category.size() || auth_3d_uid == auth_3d_id.get_uid()) + return; + + if (load_category.compare(category)) { + if (category.size()) { + auth_3d_data_unload_category(category.c_str()); + for (uint32_t& i : obj_sets) + object_storage_unload_set(i); + obj_sets.clear(); + } + auth_3d_data_load_category(load_category.c_str()); + category.assign(load_category); + if (window.obj_link) + field_394 = 1; + } + + if (!auth_3d_data_check_category_loaded(category.c_str())) + return; + + if (field_394 == 1) { + data_struct* aft_data = &data_list[DATA_AFT]; + auth_3d_database* aft_auth_3d_db = &aft_data->data_ft.auth_3d_db; + object_database* aft_obj_db = &aft_data->data_ft.obj_db; + + auth_3d_data_get_obj_sets_from_category(category, obj_sets, aft_auth_3d_db, aft_obj_db); + + for (uint32_t& i : obj_sets) + object_storage_load_set(aft_data, aft_obj_db, i); + field_394 = 2; + } + else if (field_394 == 2) { + bool wait_load = false; + for (uint32_t& i : obj_sets) + if (object_storage_load_obj_set_check_not_read(i)) + wait_load = true; + + if (!wait_load) + field_394 = 0; + } + + if (!field_394) { + data_struct* aft_data = &data_list[DATA_AFT]; + auth_3d_database* aft_auth_3d_db = &aft_data->data_ft.auth_3d_db; + + auth_3d_id.unload_id(rctx_ptr); + auth_3d_id = auth_3d_data_load_uid(auth_3d_uid, aft_auth_3d_db); + if (auth_3d_id.check_not_empty()) { + auth_3d_id.set_enable(false); + auth_3d_id.read_file(aft_auth_3d_db); + field_1D4 = 1; + } + auth_3d_uid = -1; + return; + } +} + +void Auth3dTestTask::SetStage() { + if (window.stage_link_change) { + 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 char* name = auth_3d_data_get_uid_name(auth_3d_uid, aft_auth_3d_db); + size_t name_length = utf8_length(name); + if (name && name_length >= 3) { + size_t v5 = name_length - 2; + const char* v6 = name; + while (v5) { + const char* v7 = (const char*)memchr(v6, 'S', v5); + if (!v7) + break; + + if (memcmp(v7, "STG", min_def(v5, 3))) { + v5 += v6 - v7 - 1; + v6 = v7 + 1; + continue; + } + + size_t v16 = v7 - name; + if (v16 == -1 || v16 >= name_length || name_length == v16) + break; + + size_t v17 = name_length - v16; + for (const char* i = name + v16; v17; ) { + const char* v20 = (const char*)memchr(i, '_', v17); + if (!v20) + break; + + if (*v20 != '_') { + v17 += i - v20 - 1; + i = v20 + 1; + continue; + } + + size_t v24 = v20 - name; + if (v24 != -1) { + std::string v30 = std::string(name + v16, v24 - v16); + int32_t stage_index = aft_stage_data->get_stage_index(v30.c_str()); + if (this->stage_index != stage_index) + load_stage_index = stage_index; + } + break; + } + break; + } + } + } + + if (load_stage_index != -1 && load_stage_index != stage_index) { + task_stage_set_stage_index(load_stage_index); + stage_index = load_stage_index; + load_stage_index = -1; + } +} auth_3d_test_window_category::auth_3d_test_window_category() : name(), index() { @@ -185,7 +447,7 @@ void Auth3dTestWindow::Window() { ImGui::PushID(&i); if (ImGui::Selectable(i.name->c_str(), _auth_3d_category_index == auth_3d_category_idx) - || ImGui::ItemKeyPressed(GLFW_KEY_ENTER, true) + || ImGui::ItemKeyPressed(ImGuiKey_Enter) || (ImGui::IsItemFocused() && _auth_3d_category_index != auth_3d_category_idx)) { auth_3d_category_index = -1; _auth_3d_category_index = auth_3d_category_idx; @@ -238,7 +500,7 @@ void Auth3dTestWindow::Window() { ImGui::PushID(&j); if (ImGui::Selectable(j.name->c_str(), _auth_3d_index == auth_3d_idx) - || ImGui::ItemKeyPressed(GLFW_KEY_ENTER, true) + || ImGui::ItemKeyPressed(ImGuiKey_Enter) || (ImGui::IsItemFocused() && _auth_3d_index != auth_3d_idx)) { auth_3d_index = -1; _auth_3d_index = auth_3d_idx; @@ -344,7 +606,7 @@ void Auth3dTestWindow::Window() { ImGui::PushID(i); if (ImGui::Selectable(i, stage_index == stage_idx) - || ImGui::ItemKeyPressed(GLFW_KEY_ENTER, true) + || ImGui::ItemKeyPressed(ImGuiKey_Enter) || (ImGui::IsItemFocused() && stage_index != stage_idx)) stage_index = stage_idx; ImGui::PopID(); @@ -379,6 +641,21 @@ void Auth3dTestWindow::Window() { ImGui::End(); } +void auth_3d_test_task_init() { + auth_3d_test_task = new Auth3dTestTask; +} + +void auth_3d_test_task_disp_chara() { + auth_3d_test_task->DispChara(); +} + +void auth_3d_test_task_free() { + if (auth_3d_test_task) { + delete auth_3d_test_task; + auth_3d_test_task = 0; + } +} + void auth_3d_test_window_init() { auth_3d_test_window = new Auth3dTestWindow; } diff --git a/src/ReDIVA/data_test/auth_3d_test.hpp b/src/ReDIVA/data_test/auth_3d_test.hpp index ba7fe22d..27c22c74 100644 --- a/src/ReDIVA/data_test/auth_3d_test.hpp +++ b/src/ReDIVA/data_test/auth_3d_test.hpp @@ -7,8 +7,66 @@ #include #include "../../KKdLib/default.hpp" +#include "../../CRE/auth_3d.hpp" #include "../task_window.hpp" +class Auth3dTestTask : public app::Task { +public: + int32_t field_1C0; + int32_t field_1C4; + int32_t field_1C8; + int32_t field_1CC; + bool field_1D0; + int32_t field_1D4; + ::auth_3d_id auth_3d_id; + int32_t auth_3d_uid; + bool repeat; + bool left_right_reverse; + bool field_1E3; + int32_t field_1E4; + bool field_1E8; + bool field_1E9; + bool black_mask_listener; + bool field_1EB; + int32_t field_1EC; + uint32_t effcmn_obj_set; + int32_t field_210; + bool field_328; + bool field_329; + bool field_32A; + int32_t stage_index; + int32_t load_stage_index; + vec3 trans_value; + float_t rot_y_value; + std::vector> field_348; + bool field_388; + int32_t field_38C; + bool field_390; + int32_t field_394; + std::string category; + std::string load_category; + std::vector obj_sets; + + struct Window { + bool stage_link_change; + bool obj_link; + + Window(); + } window; + + Auth3dTestTask(); + virtual ~Auth3dTestTask() override; + + virtual bool Init() override; + virtual bool Ctrl() override; + virtual bool Dest() override; + + void DispAuth3dChara(::auth_3d_id& id); + void DispChara(); + void SetAuth3dId(); + void SetStage(); +}; + struct auth_3d_test_window_uid { std::string* name; int32_t uid; @@ -53,7 +111,12 @@ public: virtual void Window() override; }; +extern Auth3dTestTask* auth_3d_test_task; extern Auth3dTestWindow* auth_3d_test_window; +extern void auth_3d_test_task_init(); +extern void auth_3d_test_task_disp_chara(); +extern void auth_3d_test_task_free(); + extern void auth_3d_test_window_init(); extern void auth_3d_test_window_free(); diff --git a/src/ReDIVA/data_test/glitter_test.cpp b/src/ReDIVA/data_test/glitter_test.cpp index 98c24197..47ac4384 100644 --- a/src/ReDIVA/data_test/glitter_test.cpp +++ b/src/ReDIVA/data_test/glitter_test.cpp @@ -163,25 +163,25 @@ void TaskDataTestGlitterParticle::Window() { input_reset = true; w = ImGui::GetContentRegionAvailWidth(); - if (ImGui::ButtonEnterKeyPressed("Play (F)", { w, 0.0f }) || ImGui::IsKeyPressed(GLFW_KEY_F)) + if (ImGui::ButtonEnterKeyPressed("Play (F)", { w, 0.0f }) || ImGui::IsKeyPressed(ImGuiKey_F)) input_play = true; w = ImGui::GetContentRegionAvailWidth(); - if (ImGui::ButtonEnterKeyPressed("Stop (V)", { w, 0.0f }) || ImGui::IsKeyPressed(GLFW_KEY_V)) + if (ImGui::ButtonEnterKeyPressed("Stop (V)", { w, 0.0f }) || ImGui::IsKeyPressed(ImGuiKey_V)) input_stop = true; bool pause = Glitter::glt_particle_manager->GetPause(); ImGui::CheckboxEnterKeyPressed("Pause (G)", &pause); - if (ImGui::IsKeyPressed(GLFW_KEY_G)) + if (ImGui::IsKeyPressed(ImGuiKey_G)) pause ^= true; Glitter::glt_particle_manager->SetPause(pause); ImGui::CheckboxEnterKeyPressed("Auto (T)", &auto_and_repeat); - if (ImGui::IsKeyPressed(GLFW_KEY_T)) + if (ImGui::IsKeyPressed(ImGuiKey_T)) auto_and_repeat ^= true; ImGui::CheckboxEnterKeyPressed("PV Mode (P)", &pv_mode); - if (ImGui::IsKeyPressed(GLFW_KEY_P)) + if (ImGui::IsKeyPressed(ImGuiKey_P)) pv_mode ^= true; ImGui::Separator(); diff --git a/src/ReDIVA/data_test/motion_test.cpp b/src/ReDIVA/data_test/motion_test.cpp new file mode 100644 index 00000000..b5e9556b --- /dev/null +++ b/src/ReDIVA/data_test/motion_test.cpp @@ -0,0 +1,783 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#include "motion_test.hpp" +#include "../../KKdLib/io/path.hpp" +#include "../../KKdLib/prj/algorithm.hpp" +#include "../../KKdLib/sort.hpp" +#include "../../KKdLib/str_utils.hpp" +#include "../../CRE/clear_color.hpp" +#include "../../CRE/data.hpp" +#include "../../CRE/hand_item.hpp" +#include "../../CRE/render_context.hpp" +#include "../../CRE/stage.hpp" +#include "../imgui_helper.hpp" +#include "../input.hpp" +#include "../task_window.hpp" +#include "auth_3d_test.hpp" + +extern int32_t width; +extern int32_t height; +extern render_context* rctx_ptr; + +DataTestMot* data_test_mot; +DtmMot* dtm_mot_array; +DtmEqVs* dtm_eq_vs_array; + +std::vector motion_test_objset; + +static void dtm_mot_array_set_reset_mot(); + +static bool motion_test_objset_check_not_read(); +static void motion_test_objset_load(); +static void motion_test_objset_unload(); + +DataTestMot::Data::Divide::Divide() { + array[0] = 0.0f; + array[1] = 0.0f; + array[2] = 0.0f; +} + +DataTestMot::Data::Step::Step() { + array[0] = 0.0f; + array[1] = 0.0f; + array[2] = 0.0f; + array[3] = 1.0f; +} + +DataTestMot::Data::Data() : chara_index_p1(), chara_index_p2(), curr_chara_index_p1(), +curr_chara_index_p2(), module_index_p1(), module_index_p2(), curr_module_index_p1(), +curr_module_index_p2(), motion_set_index_p1(), motion_set_index_p2(), +curr_motion_set_index_p1(), curr_motion_set_index_p2(), motion_index_p1(), +motion_index_p2(), curr_motion_index_p1(), curr_motion_index_p2(), rot_y_p1(), rot_y_p2(), +trans_x_p1(), trans_x_p2(), pre_offset_p1(), post_offset_p1(), pre_offset_p2(), post_offset_p2(), +start_frame_p1(), start_frame_p2(), type(), reset_mot(), reset_cam(), reload_data(), +sync_frame(), field_A8(), field_A9(), running(), field_AC(), field_B0(), sync_1p_frame() { + field_AA = true; +} + +DataTestMot::DataTestMot() { + +} + +DataTestMot::~DataTestMot() { + +} + +bool DataTestMot::Init() { + clear_color = { 0x60, 0x60, 0x60, 0xFF }; + set_clear_color = true; + + camera* cam = rctx_ptr->camera; + cam->reset(); + cam->set_view_point({ 0.0f, 1.0f, 3.45f }); + cam->set_interest({ 0.0f, 1.0f, 0.0f }); + + //DataTestMotDw::InitByCharaID(0, &dtm_mot_array[-]); + //DataTestMotDw::InitByCharaID(1, &dtm_mot_array[1]); + //data_test_mot_ctrl_dw_init(); + //data_test_mot_a3d_dw_init(); + data.reset_cam = true; + data.field_A8 = true; + dtm_eq_vs_array[0].AddTask(0, data.chara_index_p1); + dtm_eq_vs_array[1].AddTask(1, data.chara_index_p1); + motion_test_objset_load(); + return true; +} + +bool DataTestMot::Ctrl() { + data_struct* aft_data = &data_list[DATA_AFT]; + motion_database* aft_mot_db = &aft_data->data_ft.mot_db; + + //data_mot_test_dw_array_get(0)->sub_14028D8B0(); + + bool v3 = false; + bool v2 = false; + + if (data.reload_data) { + data.reload_data = false; + v3 = true; + v2 = true; + } + + if (data.chara_index_p1 != data.curr_chara_index_p1) { + data.curr_chara_index_p1 = data.chara_index_p1; + dtm_eq_vs_array[0].SetCharaIndexModuleIndex(data.chara_index_p1, data.module_index_p1); + v3 = true; + v2 = true; + } + + if (data.chara_index_p2 != data.curr_chara_index_p2) { + data.curr_chara_index_p2 = data.chara_index_p2; + dtm_eq_vs_array[1].SetCharaIndexModuleIndex(data.chara_index_p2, data.module_index_p2); + v3 = true; + v2 = true; + } + + if (data.module_index_p1 != data.curr_module_index_p1) { + data.curr_module_index_p1 = data.module_index_p1; + dtm_eq_vs_array[0].SetCharaIndexModuleIndex(data.chara_index_p1, data.module_index_p1); + v3 = true; + v2 = true; + } + + if (data.module_index_p2 != data.curr_module_index_p2) { + data.curr_module_index_p2 = data.module_index_p2; + dtm_eq_vs_array[1].SetCharaIndexModuleIndex(data.chara_index_p2, data.module_index_p2); + v3 = true; + v2 = true; + } + + if (data.motion_set_index_p1 != data.curr_motion_set_index_p1) { + data.curr_motion_set_index_p1 = data.motion_set_index_p1; + + /*data_mot_test_dw_array_get(0)->field_D8(); + + uint32_t v16 = motion_database_set_indices_get_id(data.motion_set_index_p1); + uint32_t v18 = motion_database_get_set_motion_count(v16); + for (uint32_t i = 0; i < v18; i++) { + uint32_t v20 = motion_database_get_motion_id(v16, i); + const char* v21 = motion_database_get_motion_name_by_motion_index(v20); + data_mot_test_dw_array_get(0)->field_E0(v21); + } + + data_mot_test_dw_array_get(0)->sub_14028EB80();*/ + + data.curr_motion_index_p1 = 0; + data.motion_index_p1 = 0; + v3 = true; + v2 = true; + } + + if (data.motion_set_index_p2 != data.curr_motion_set_index_p2) { + data.curr_motion_set_index_p2 = data.motion_set_index_p2; + + /*data_mot_test_dw_array_get(1)->field_D8(); + + uint32_t v24 = motion_database_set_indices_get_id(data.motion_set_index_p2); + uint32_t v18 = motion_database_get_set_motion_count(v24); + for (uint32_t i = 0; i < v18; i++) { + uint32_t v28 = motion_database_get_motion_id(v24, i); + const char* v29 = motion_database_get_motion_name_by_motion_index(v28); + data_mot_test_dw_array_get(0)->field_E0(v29); + } + + data_mot_test_dw_array_get(1)->sub_14028EB80();*/ + + data.curr_motion_index_p2 = 0; + data.motion_index_p2 = 0; + + v3 = true; + v2 = true; + } + + bool v59 = false; + if (data.motion_index_p1 != data.curr_motion_index_p1) { + data.curr_motion_index_p1 = data.motion_index_p1; + dtm_mot_array[0].SetMotion(data.motion_set_index_p1, data.motion_index_p1); + dtm_mot_array[1].SetMotion(data.motion_set_index_p2, data.motion_index_p2); + dtm_mot_array[0].SetChangeMotion(); + dtm_mot_array[1].SetChangeMotion(); + v59 = true; + } + + if (data.motion_index_p2 != data.curr_motion_index_p2) { + data.curr_motion_index_p2 = data.motion_index_p2; + dtm_mot_array[0].SetMotion(data.motion_set_index_p1, data.motion_index_p1); + dtm_mot_array[1].SetMotion(data.motion_set_index_p2, data.motion_index_p2); + dtm_mot_array[0].SetChangeMotion(); + dtm_mot_array[1].SetChangeMotion(); + } + + dtm_mot_array[0].SetRotationY(data.rot_y_p1); + dtm_mot_array[1].SetRotationY(data.rot_y_p2); + dtm_mot_array[0].SetTrans({ data.trans_x_p1, 0.0f, 0.0f }); + dtm_mot_array[1].SetTrans({ data.trans_x_p2, 0.0f, 0.0f }); + dtm_mot_array[0].SetOffset(data.pre_offset_p1, data.post_offset_p1); + dtm_mot_array[1].SetOffset(data.pre_offset_p2, data.post_offset_p2); + dtm_mot_array[0].SetStartFrame(data.start_frame_p1); + dtm_mot_array[1].SetStartFrame(data.start_frame_p2); + + for (int32_t i = 0; i < 3; i++) { + dtm_mot_array[0].SetDivide(i, data.divide_p1.array[i]); + dtm_mot_array[1].SetDivide(i, data.divide_p2.array[i]); + } + + for (int32_t i = 0; i < 4; i++) { + dtm_mot_array[0].SetStep(i, data.step_p1.array[i]); + dtm_mot_array[1].SetStep(i, data.step_p2.array[i]); + } + + switch (data.type) { + case 0: { + dtm_mot_array[0].SetLoop(false); + dtm_mot_array[1].SetLoop(false); + + dtm_mot_array[0].SetPlay(true); + dtm_mot_array[1].SetPlay(true); + } break; + case 1: { + dtm_mot_array[0].SetLoop(true); + dtm_mot_array[1].SetLoop(true); + + dtm_mot_array[0].SetPlay(true); + dtm_mot_array[1].SetPlay(true); + } break; + case 2: { + dtm_mot_array[0].SetLoop(true); + dtm_mot_array[1].SetLoop(true); + + bool play = false;//input_state_get(0)->sub_14018D540(84) != 0; + dtm_mot_array[0].SetPlay(play); + dtm_mot_array[1].SetPlay(play); + } break; + } + + if (data.reset_mot) { + data.reset_mot = false; + + dtm_mot_array[0].SetResetMot(); + dtm_mot_array[1].SetResetMot(); + } + + if (data.sync_frame) { + int32_t chara_id = dtm_mot_array[1].GetFrameCount() > dtm_mot_array[0].GetFrameCount() ? 1 : 0; + float_t frame = dtm_mot_array[chara_id].GetFrame(); + dtm_mot_array[0].SetFrame(frame); + dtm_mot_array[1].SetFrame(frame); + } + + if (v3) + dtm_mot_array[0].DelTask(); + else + dtm_mot_array[0].AddTask(data.chara_index_p1, + data.module_index_p1, data.motion_set_index_p1, data.motion_index_p1); + + if (v2) + dtm_mot_array[1].DelTask(); + else + dtm_mot_array[1].AddTask(data.chara_index_p2, + data.module_index_p2, data.motion_set_index_p2, data.motion_index_p2); + + if (data.reset_cam) { + data.reset_cam = false; + + camera* cam = rctx_ptr->camera; + cam->reset(); + + if (data.field_A9) { + cam->set_view_point({ 0.0f, 1.0f, 3.45f }); + cam->set_interest({ 0.0f, 1.0f, 0.0f }); + cam->set_view_point({ -3.79f, 0.71f, 1.0f }); + cam->set_interest({ 0.0f, 0.96f, 0.0f }); + cam->set_fov(32.8124985652134); + } + else { + cam->set_view_point({ 0.0f, 1.0f, 3.45f }); + cam->set_interest({ 0.0f, 1.0f, 0.0f }); + cam->set_fov(32.8124985652134); + } + } + + if (data.field_A8) { + data.field_A8 = false; + //sub_1401F9510(data.field_A9 ? 32.2673416137695 : 32.8125); + } + + /*if (data.running && dtm_mot_array[0].sub_140291B70() && !v59 + && data.motion_index_p1 < aft_mot_db->motion_set[data.motion_set_index_p1].motion.size()) { + data.curr_motion_index_p1 = data.motion_index_p1; + data.motion_index_p1 = data.motion_index_p1 + 1; + dtm_mot_array[0].SetMotion(data.motion_set_index_p1, data.motion_index_p1); + dtm_mot_array[1].SetMotion(data.motion_set_index_p2, data.motion_index_p2); + dtm_mot_array[0].sub_140291F20(); + dtm_mot_array[1].sub_140291F20(); + + //data_mot_test_dw_array_get(0)->sub_14028EF90(data.motion_index_p1); + }*/ + + motion_test_objset_check_not_read(); + sub_140286280(); + return false; +} + +bool DataTestMot::Dest() { + clear_color = { 0x00, 0x00, 0x00, 0xFF }; + set_clear_color = true; + + dtm_mot_array[0].DelTask(); + dtm_mot_array[1].DelTask(); + //data_mot_test_dw_array_get(0)->field_D0(); + //data_mot_test_dw_array_get(1)->field_D0(); + //data_test_mot_ctrl_dw_get()->field_D0(); + //data_test_mot_a3d_dw()->field_D0(); + //sub_140263340(); + dtm_eq_vs_array[0].DelTask(); + dtm_eq_vs_array[1].DelTask(); + motion_test_objset_unload(); + //data_mot_test_a3d_get()->DelTask(); + return true; +} + +void DataTestMot::Disp() { + +} + +void DataTestMot::sub_140286280() { + /*dtm_mot_array[0].sub_1402922C0(true); + dtm_mot_array[0].sub_1402922C0(true); + if (this->data.sync_frame && (!dtm_mot_array[0].sub_140291C10() || !dtm_mot_array[1].sub_140291C10())) { + dtm_mot_array[0].sub_1402922C0(false); + dtm_mot_array[0].sub_1402922C0(false); + }*/ +} + +DtmMot::DtmMot() : rob_bone_data(), field_7C(), field_A8(), pre_offset(), post_offset(), divide(), +loop(), change_motion(), use_opd(), field_D6(), reset_mot(), save_only_start_frame(), state(), +frame(), field_E4(), field_E8(), start_frame(), a_frame(), b_frame(), ab_loop(), field_2C960() { + chara_id = -1; + chara_index = CHARA_MAX; + module_index = 502; + motion_set_id = -1; + motion_id = -1; + step[0] = 0.0f; + step[1] = 0.0f; + step[2] = 0.0f; + step[3] = 1.0f; + play = true; + field_D2 = true; + disp = true; + partial_mot = true; + pv_id = -1; + field_100 = true; +} + +DtmMot::~DtmMot() { + +} + +bool DtmMot::Init() { + frame = 0.0f; + field_E4 = 1.0f; + state = 1; + field_E8 = 0; + + if (chara_index < CHARA_MIKU || chara_index >= CHARA_MAX || module_index >= 502) { + this->state = 0; + return true; + } + + data_struct* aft_data = &data_list[DATA_AFT]; + motion_database* aft_mot_db = &aft_data->data_ft.mot_db; + + if (!field_7C) { + int32_t motion_set_index = this->motion_set_index; + int32_t motion_index = this->motion_index; + if (aft_mot_db->motion_set.size() <= motion_set_index) { + state = 0; + return true; + } + + motion_set_id = aft_mot_db->motion_set[motion_set_index].id; + if (aft_mot_db->motion_set[motion_set_index].motion.size() <= motion_index) { + state = 0; + return true; + } + motion_id = aft_mot_db->motion_set[motion_set_index].motion[motion_index].id; + } + + pv_id = DtmMot::ConvertMotionSetNameToPVID(aft_mot_db->get_motion_set_name(motion_set_id)); + field_D6 = false; + return true; +} + +bool DtmMot::Ctrl() { + return false; +} + +bool DtmMot::Dest() { + return true; +} + +void DtmMot::Basic() { + +} + +bool DtmMot::AddTask(::chara_index chara_index, + int32_t module_index, int32_t motion_set_id, int32_t motion_id) { + return true; +} + +bool DtmMot::AddTask(::chara_index chara_index, + int32_t module_index, int32_t motion_id) { + return true; +} + +bool DtmMot::DelTask() { + if (app::TaskWork::CheckTaskReady(this)) + return app::Task::DelTask(); + return false; +} + +float_t DtmMot::GetFrame() { + if (state == 13) + return frame; + return 0.0f; +} + +float_t DtmMot::GetFrameCount() { + if (state == 13 && rob_bone_data) + return rob_bone_data->get_frame_count(); + return 0.0f; +} + +float_t DtmMot::GetStep() { + return step[3]; +} + +void DtmMot::SetChangeMotion() { + change_motion = true; +} + +void DtmMot::SetDisp(bool value) { + disp = value; +} + +void DtmMot::SetDivide(int32_t index, float_t value) { + if (index < 3) + divide[index] = value; +} + +void DtmMot::SetFrame(float_t value) { + frame = value; +} + +void DtmMot::SetLoop(bool value) { + loop = value; +} + +void DtmMot::SetMotion(uint32_t motion_set_index, uint32_t motion_index) { + data_struct* aft_data = &data_list[DATA_AFT]; + motion_database* aft_mot_db = &aft_data->data_ft.mot_db; + + state = 0; + + if (motion_set_index >= aft_mot_db->motion_set.size()) + return; + + const motion_set_info* set_info = &aft_mot_db->motion_set[motion_set_index]; + motion_set_id = set_info->id; + + if (motion_index >= set_info->motion.size()) + return; + + state = 7; + frame = 0.0f; + motion_id = set_info->motion[motion_index].id; +} + +void DtmMot::SetOffset(float_t pre_offset, float_t post_offset) { + this->pre_offset = pre_offset; + this->post_offset = post_offset; +} + +void DtmMot::SetPlay(bool value) { + partial_mot = value; +} + +void DtmMot::SetPartialMot(bool value) { + play = value; +} + +void DtmMot::SetResetMot() { + reset_mot = true; +} + +void DtmMot::SetRotationY(float_t value) { + rotatation.y = value; +} + +void DtmMot::SetSaveOnlyStartFrame(bool value) { + save_only_start_frame = value; +} + +void DtmMot::SetStartFrame(float_t value) { + start_frame = value; +} + +void DtmMot::SetStep(int32_t index, float_t value) { + if (index < 4) + step[index] = value; +} + +void DtmMot::SetTrans(const vec3& value) { + trans = value; +} + +void DtmMot::SetUseOpd(bool value) { + use_opd = value; +} + +int32_t DtmMot::ConvertMotionSetNameToPVID(const char* set_name) { + if (!set_name) + return -1; + + size_t length = utf8_length(set_name); + if (length < 5) + return -1; + + if (memcmp(set_name, "PV", 2)) + return -1; + + if (set_name[2] < '0' || set_name[2] > '9' + || set_name[3] < '0' || set_name[3] > '9' + || set_name[4] < '0' || set_name[4] > '9') + return -1; + + int32_t pv_id = (set_name[2] - '0') * 100; + pv_id += (set_name[3] - '0') * 10; + pv_id += set_name[4] - '0'; + return pv_id; +} + +DtmEqVs::DtmEqVs() : chara_id(), chara_index(), curr_chara_index(), +module_index(), curr_module_index(), field_80() { + state = -1; +} + +DtmEqVs::~DtmEqVs() { + +} + +bool DtmEqVs::Init() { + state = 0; + return true; +} + +bool DtmEqVs::Ctrl() { + rob_chara_item_cos_data* item_cos_data = rob_chara_array_get_item_cos_data(chara_id); + rob_chara* rob_chr = rob_chara_array_get(chara_id); + switch (state) { + case 0: + //data_test_equip_dw_sub_14025FCC0(this->chara_id); + //data_test_equip_dw_sub_14025FA80(this->chara_id, item_cos_data->get_cos()); + state = 3; + case 3: + if (false /*dtm_mot_array_sub_140287740()*/) { + sub_140262050(); + sub_140261FF0(); + + int32_t item_no = ITEM_NONE;//data_test_equip_dw_sub_14025FB20(this->chara_id); + ::item_cos_data temp_cos; + if (rob_chr) + temp_cos = *rob_chr->item_cos_data.get_cos(); + + bool v2 = false; + /*if (data_test_equip_dw_sub_14025FAF0(chara_id)) { + item_cos_data->set_chara_index_item(chara_index, item_no); + v2 = true; + }*/ + + /*if (data_test_equip_dw_sub_14025FB80(chara_id)) { + item_cos_data->set_chara_index_item_zero(chara_index, item_no); + v2 = true; + }*/ + + const ::item_cos_data* cos = 0;//data_test_equip_dw_get_cos(chara_id); + if (cos) { + item_cos_data->set_chara_index_item_nos(chara_index, cos->arr); + v2 = true; + } + + /*if (sub_1402CEEC0(chara_id)) { + v2 = true; + sub_1402CF8F0(); + }*/ + + //data_test_equip_dw_sub_14025FBE0(chara_id); + + if (v2) { + if (rob_chr) + task_rob_load_append_free_req_data_obj(rob_chr->chara_index, &temp_cos); + rob_chara_array_reset_bone_data_item_equip(chara_id); + task_rob_manager_hide_task(); + state = 1; + } + } + break; + case 1: + if (rob_chr->chara_index) { + task_rob_load_append_load_req_data_obj(rob_chr->chara_index, rob_chr->item_cos_data.get_cos()); + state = 2; + } + break; + case 2: + if (!task_rob_load_check_load_req_data()) { + task_rob_manager_run_task(); + if (rob_chara_array_get(chara_id)) { + data_struct* aft_data = &data_list[DATA_AFT]; + bone_database* aft_bone_data = &aft_data->data_ft.bone_data; + object_database* aft_obj_db = &aft_data->data_ft.obj_db; + item_cos_data->reload_items(chara_id, aft_bone_data, aft_data, aft_obj_db); + } + auth_3d_test_task_disp_chara(); + dtm_mot_array_set_reset_mot(); + this->state = 3; + } + break; + } + return false; +} + +bool DtmEqVs::Dest() { + return true; +} + +bool DtmEqVs::AddTask(int32_t chara_id, ::chara_index chara_index) { + this->chara_id = chara_id; + this->chara_index = chara_index; + this->curr_chara_index = chara_index; + module_index = 0; + curr_module_index = 0; + this->field_80 = 0; + //data_test_equip_dw_set_chara_index(chara_id, chara_index); + //data_test_equip_dw_set_module_index(chara_id, module_index); + return app::TaskWork::AddTask(this, "DATA TEST EQUIP MANAGER FOR VS"); + return true; +} + +bool DtmEqVs::DelTask() { + return app::Task::DelTask(); +} + +void DtmEqVs::SetCharaIndexModuleIndex(::chara_index chara_index, int32_t module_index) { + this->chara_index = chara_index; + this->module_index = module_index; + //data_test_equip_dw_set_chara_index(chara_id, chara_index); + //data_test_equip_dw_set_module_index(chara_id, module_index); +} + +void DtmEqVs::sub_140261FF0() { + if (chara_index == curr_chara_index && module_index == curr_module_index) + return; + + curr_chara_index = chara_index; + curr_module_index = module_index; + const item_cos_data* cos = item_table_handler_array_get_item_cos_data_by_module_index(chara_index, module_index); + if (!check_module_index_is_501(curr_module_index) || !cos) { + //data_test_equip_dw_sub_14025FCC0(chara_id); + //data_test_equip_dw_sub_14025FA80(chara_id, cos); + } +} + +void DtmEqVs::sub_140262050() { + return; + + int32_t v2 = 0;//data_test_equip_dw_sub_14025FAC0(chara_id); + /*if (v2 == field_80) + return;*/ + + rob_chara_item_equip* rob_itm_equip = rob_chara_array_get_item_equip(chara_id); + bool disp = false; + item_id id; + switch (v2) { + default: + id = ITEM_NONE; + disp = true; + break; + case 1: + id = ITEM_NONE; + break; + case 2: + id = ITEM_OUTER; + break; + case 3: + id = ITEM_PANTS; + break; + } + + for (int32_t i = ITEM_ATAMA; i < ITEM_MAX; i++) + if (rob_itm_equip->get_object_info((item_id)i).not_null()) + rob_itm_equip->set_disp((item_id)i, true); + + for (int32_t i = ITEM_ATAMA; i < ITEM_MAX; i++) + if (i != id && (!disp || i != rob_itm_equip->field_D4)) + rob_itm_equip->set_disp((item_id)i, disp); + + field_80 = v2; +} + +void motion_test_init() { + if (!data_test_mot) + data_test_mot = new DataTestMot; + + if (!dtm_mot_array) + dtm_mot_array = new DtmMot[2]; + + if (!dtm_eq_vs_array) + dtm_eq_vs_array = new DtmEqVs[2]; + + motion_test_objset = {}; +} + +void motion_test_free() { + motion_test_objset.clear(); + motion_test_objset.shrink_to_fit(); + + if (dtm_eq_vs_array) { + delete[] dtm_eq_vs_array; + dtm_eq_vs_array = 0; + } + + if (dtm_mot_array) { + delete[] dtm_mot_array; + dtm_mot_array = 0; + } + + if (data_test_mot) { + delete data_test_mot; + data_test_mot = 0; + } +} + +static void dtm_mot_array_set_reset_mot() { + dtm_mot_array[0].SetResetMot(); + dtm_mot_array[1].SetResetMot(); +} + +static bool motion_test_objset_check_not_read() { + for (uint32_t i : motion_test_objset) + if (object_storage_load_obj_set_check_not_read(i)) + return true; + return false; +} + +static void motion_test_objset_load() { + motion_test_objset.clear(); + + auto& hand_items = hand_item_handler_data_get_hand_items(); + for (auto& i : hand_items) { + const hand_item& hnd_itm = i.second; + if (hnd_itm.obj_left.not_null()) + motion_test_objset.push_back(hnd_itm.obj_left.set_id); + if (hnd_itm.obj_right.not_null()) + motion_test_objset.push_back(hnd_itm.obj_right.set_id); + } + + prj::sort(motion_test_objset); + prj::unique(motion_test_objset); + + data_struct* aft_data = &data_list[DATA_AFT]; + object_database* aft_obj_db = &aft_data->data_ft.obj_db; + + for (uint32_t i : motion_test_objset) + object_storage_load_set(aft_data, aft_obj_db, i); +} + +static void motion_test_objset_unload() { + for (uint32_t i : motion_test_objset) + object_storage_unload_set(i); + motion_test_objset.clear(); +} diff --git a/src/ReDIVA/data_test/motion_test.hpp b/src/ReDIVA/data_test/motion_test.hpp new file mode 100644 index 00000000..bf3c0667 --- /dev/null +++ b/src/ReDIVA/data_test/motion_test.hpp @@ -0,0 +1,198 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#pragma once + +#include "../../CRE/rob/rob.hpp" +#include "../../KKdLib/default.hpp" +#include "../task_window.hpp" + +class DataTestMot : public app::Task { +public: + struct Data { + struct Divide { + float_t array[3]; + + Divide(); + }; + + struct Step { + float_t array[4]; + + Step(); + }; + + ::chara_index chara_index_p1; + ::chara_index chara_index_p2; + ::chara_index curr_chara_index_p1; + ::chara_index curr_chara_index_p2; + uint32_t module_index_p1; + uint32_t module_index_p2; + uint32_t curr_module_index_p1; + uint32_t curr_module_index_p2; + uint32_t motion_set_index_p1; + uint32_t motion_set_index_p2; + uint32_t curr_motion_set_index_p1; + uint32_t curr_motion_set_index_p2; + uint32_t motion_index_p1; + uint32_t motion_index_p2; + uint32_t curr_motion_index_p1; + uint32_t curr_motion_index_p2; + float_t rot_y_p1; + float_t rot_y_p2; + float_t trans_x_p1; + float_t trans_x_p2; + float_t pre_offset_p1; + float_t post_offset_p1; + float_t pre_offset_p2; + float_t post_offset_p2; + Divide divide_p1; + Divide divide_p2; + Step step_p1; + Step step_p2; + float_t start_frame_p1; + float_t start_frame_p2; + int32_t type; + bool reset_mot; + bool reset_cam; + bool reload_data; + bool sync_frame; + bool field_A8; + bool field_A9; + bool field_AA; + bool running; + int32_t field_AC; + int32_t field_B0; + bool sync_1p_frame; + + Data(); + }; + + Data data; + + DataTestMot(); + virtual ~DataTestMot() override; + + virtual bool Init() override; + virtual bool Ctrl() override; + virtual bool Dest() override; + virtual void Disp() override; + + void sub_140286280(); +}; + +class DtmMot : public app::Task { +public: + rob_chara_bone_data* rob_bone_data; + int32_t chara_id; + int32_t field_7C; + ::chara_index chara_index; + int32_t module_index; + union { + int32_t motion_set_id; + int32_t motion_set_index; + }; + union { + int32_t motion_id; + int32_t motion_index; + }; + vec3 trans; + vec3 rotatation; + float_t field_A8; + float_t pre_offset; + float_t post_offset; + float_t step[4]; + float_t divide[3]; + bool loop; + bool play; + bool field_D2; + bool change_motion; + bool disp; + bool use_opd; + bool field_D6; + bool reset_mot; + bool partial_mot; + bool save_only_start_frame; + int32_t state; + float_t frame; + float_t field_E4; + bool field_E8; + float_t start_frame; + float_t a_frame; + float_t b_frame; + bool ab_loop; + int32_t pv_id; + bool field_100; + p_file_handler field_108; + //pv_game_dsc_data field_110; + std::vector set_motion; + int32_t field_2C960; + + DtmMot(); + virtual ~DtmMot() override; + + virtual bool Init() override; + virtual bool Ctrl() override; + virtual bool Dest() override; + virtual void Basic() override; + + virtual bool AddTask(::chara_index chara_index, + int32_t module_index, int32_t motion_set_id, int32_t motion_id); + virtual bool AddTask(::chara_index chara_index, + int32_t module_index, int32_t motion_id); + virtual bool DelTask(); + + float_t GetFrame(); + float_t GetFrameCount(); + float_t GetStep(); + void SetChangeMotion(); + void SetDisp(bool value); + void SetDivide(int32_t index, float_t value); + void SetFrame(float_t value); + void SetLoop(bool value); + void SetMotion(uint32_t motion_set_index, uint32_t motion_index); + void SetOffset(float_t pre_offset, float_t post_offset); + void SetPartialMot(bool value); + void SetPlay(bool value); + void SetResetMot(); + void SetRotationY(float_t value); + void SetSaveOnlyStartFrame(bool value); + void SetStartFrame(float_t value); + void SetStep(int32_t index, float_t value); + void SetTrans(const vec3& value); + void SetUseOpd(bool value); + + static int32_t ConvertMotionSetNameToPVID(const char* set_name); +}; + +class DtmEqVs : public app::Task { +public: + int32_t state; + int32_t chara_id; + ::chara_index chara_index; + ::chara_index curr_chara_index; + int32_t module_index; + int32_t curr_module_index; + int32_t field_80; + + DtmEqVs(); + virtual ~DtmEqVs() override; + + virtual bool Init() override; + virtual bool Ctrl() override; + virtual bool Dest() override; + + virtual bool AddTask(int32_t chara_id, ::chara_index chara_index); + virtual bool DelTask(); + + void SetCharaIndexModuleIndex(::chara_index chara_index, int32_t module_index); + + void sub_140261FF0(); + void sub_140262050(); +}; + +extern DataTestMot* data_test_mot; +extern DtmMot* dtm_mot_array; +extern DtmEqVs* dtm_eq_vs_array; diff --git a/src/ReDIVA/data_test/stage_test.cpp b/src/ReDIVA/data_test/stage_test.cpp index 1ae47900..4450cc36 100644 --- a/src/ReDIVA/data_test/stage_test.cpp +++ b/src/ReDIVA/data_test/stage_test.cpp @@ -10,6 +10,7 @@ #include "../../CRE/data.hpp" #include "../../CRE/render_context.hpp" #include "../../CRE/stage.hpp" +#include "../../CRE/task_effect.hpp" #include "../imgui_helper.hpp" #include "../input.hpp" #include "../task_window.hpp" @@ -17,6 +18,7 @@ extern int32_t width; extern int32_t height; +DtmStg* dtm_stg; DtwStg* dtw_stg; static int stage_test_stage_pv_quicksort_compare_func(void const* src1, void const* src2); @@ -29,139 +31,148 @@ stage_test_stage_pv::~stage_test_stage_pv() { } -DtwStg::DtwStg() : pv_id(), pv_index(), ns_index(), -other_index(), stage_index(), stage_index_load(), stage_load() { +DtmStg::DtmStg() : stage_index(), load_stage_index() { + +} + +DtmStg::~DtmStg() { + +} + +bool DtmStg::Init() { + task_stage_load_task("DATA_TEST_STG_STAGE"); + task_stage_set_stage_index(stage_index); + return true; +} + +bool DtmStg::Ctrl() { + if (task_stage_check_not_loaded()) + return false; + + if (load_stage_index != stage_index) { + task_stage_set_stage_index(load_stage_index); + stage_index = load_stage_index; + return false; + } + + task_stage_current_set_stage_display(dtw_stg->stage_display->value, 1); + task_stage_current_set_ground(dtw_stg->ground->value); + task_stage_current_set_ring(dtw_stg->ring->value); + task_stage_current_set_sky(dtw_stg->sky->value); + task_effect_parent_set_enable(dtw_stg->effect_display->value); + return false; +} + +bool DtmStg::Dest() { + task_stage_unload_task(); + return true; +} + +DtwStg::DtwStg() : Shell(0) { data_struct* aft_data = &data_list[DATA_AFT]; stage_database* aft_stage_data = &aft_data->data_ft.stage_data; std::vector& stg_data = aft_stage_data->stage_data; - pv_id = -1; - pv_index = -1; - ns_index = -1; - other_index = -1; - stage_index = -1; + dw::Widget::SetName("STAGE TEST"); - stage_index_load = -1; - stage_load = false; + dw::Composite* pv_comp = new dw::Composite(this); + pv_comp->SetLayout(new dw::RowLayout); - size_t stg_pv_count = 0; - size_t stg_ns_count = 0; - size_t stg_other_count = 0; - for (stage_data& i : stg_data) { - bool stgpv = false; - bool stgd2pv = false; - bool stgns = false; - bool stgd2ns = false; - if (i.name.size() >= 8 && !memcmp(i.name.c_str(), "STGPV", 5)) - stg_pv_count++; - else if (i.name.size() >= 10 && !memcmp(i.name.c_str(), "STGD2PV", 7)) - stg_pv_count++; - else if (i.name.size() >= 8 && !memcmp(i.name.c_str(), "STGNS", 5)) - stg_ns_count++; - else if (i.name.size() >= 10 && !memcmp(i.name.c_str(), "STGD2NS", 7)) - stg_ns_count++; + (new dw::Label(pv_comp))->SetName("PV:"); + + pv_id = new dw::ListBox(pv_comp); + pv = new dw::ListBox(pv_comp); + + dw::Composite* ns_comp = new dw::Composite(this); + ns_comp->SetLayout(new dw::RowLayout); + + (new dw::Label(ns_comp))->SetName("NS:"); + + ns = new dw::ListBox(ns_comp); + + dw::Composite* other_comp = new dw::Composite(this); + other_comp->SetLayout(new dw::RowLayout); + + (new dw::Label(other_comp))->SetName("Other:"); + + other = new dw::ListBox(other_comp); + + stage = new dw::ListBox(this); + + for (const stage_data& i : aft_stage_data->stage_data) { + const std::string& name = i.name; + stage->AddItem(name); + + std::string pv_id; + if (name.size() >= 8 && !name.find("STGPV")) + pv_id.assign(name, 5, 3); + else if (name.size() >= 10 && !name.find("STGD2PV")) + pv_id.assign(name, 7, 3); + + if (pv_id.size()) { + auto elem = pv_stage.find(pv_id); + if (elem == pv_stage.end()) + elem = pv_stage.insert({ pv_id, {} }).first; + elem->second.push_back(name); + } + else if (!name.find("STGNS") || !name.find("STGD2NS")) + ns->AddItem(name); else - stg_other_count++; + other->AddItem(name); } - stage_pv.reserve(stg_pv_count); - stage_ns.reserve(stg_ns_count); - stage_other.reserve(stg_other_count); + for (auto& i : pv_stage) + pv_id->AddItem(i.first); - for (stage_data& i : stg_data) { - bool stgpv = false; - bool stgd2pv = false; - bool stgns = false; - bool stgd2ns = false; - if (i.name.size() >= 8 && !memcmp(i.name.c_str(), "STGPV", 5)) - stgpv = true; - else if (i.name.size() >= 10 && !memcmp(i.name.c_str(), "STGD2PV", 7)) - stgd2pv = true; - else if (i.name.size() >= 8 && !memcmp(i.name.c_str(), "STGNS", 5)) - stgns = true; - else if (i.name.size() >= 10 && !memcmp(i.name.c_str(), "STGD2NS", 7)) - stgd2ns = true; + //dw::List::sub_1402F9930(this->pv_id->list, 30); + pv_id->AddSelectionListener(new dw::SelectionListenerOnHook(DtwStg::PvIdCallback)); - if (stgpv || stgd2pv) { - int32_t pv_id = 0; - int32_t ret; - if (stgpv) - ret = sscanf_s(i.name.c_str() + 5, "%03d", &pv_id); - else - ret = sscanf_s(i.name.c_str() + 7, "%03d", &pv_id); + //dw::List::sub_1402F9930(this->pv->list, 30); + pv->AddSelectionListener(new dw::SelectionListenerOnHook(DtwStg::StageCallback)); - if (ret != 1) - continue; + //dw::List::sub_1402F9930(this->ns->list, 30); + ns->list->hovered_item = 0; + ns->list->selected_item = 0; + ns->AddSelectionListener(new dw::SelectionListenerOnHook(DtwStg::StageCallback)); - std::vector::iterator stg_pv = stage_pv.begin(); - while (stg_pv != stage_pv.end()) - if (pv_id == (stg_pv++)->pv_id) - break; + //dw::List::sub_1402F9930(this->stage->list, 30); + stage->list->hovered_item = 0; + stage->list->selected_item = 0; + stage->AddSelectionListener(new dw::SelectionListenerOnHook(DtwStg::StageCallback)); - if (stg_pv == stage_pv.end()) { - stage_pv.push_back(stage_test_stage_pv(pv_id)); - stg_pv = stage_pv.end() - 1; - } + stage_display = new dw::Button(this, WIDGET_CHECKBOX); + stage_display->SetName("Stage display"); + stage_display->SetValue(true); - stg_pv->stage.push_back((int32_t)(&i - stg_data.data())); - } - else if (stgns || stgd2ns) { - int32_t ns_id = 0; - int32_t ret; - if (stgns) - ret = sscanf_s(i.name.c_str() + 5, "%03d", &ns_id); - else - ret = sscanf_s(i.name.c_str() + 7, "%03d", &ns_id); + ring = new dw::Button(this, WIDGET_CHECKBOX); + ring->SetName("[Ring]"); + ring->SetValue(true); - if (ret != 1) - continue; + ground = new dw::Button(this, WIDGET_CHECKBOX); + ground->SetName("[Ground]"); + ground->SetValue(true); - stage_ns.push_back((int32_t)(&i - stg_data.data())); - } - else - stage_other.push_back((int32_t)(&i - stg_data.data())); - } + sky = new dw::Button(this, WIDGET_CHECKBOX); + sky->SetName("[Sky]"); + sky->SetValue(true); - if (stage_pv.size()) - quicksort_custom(stage_pv.data(), stage_pv.size(), - sizeof(stage_test_stage_pv), - stage_test_stage_pv_quicksort_compare_func); + effect_display = new dw::Button(this, WIDGET_CHECKBOX); + effect_display->SetName("Effects display"); + effect_display->SetValue(true); - if (stage_pv.size()) - pv_id = stage_pv[0].pv_id; - - stage_pv = stage_pv; - stage_ns = stage_ns; - stage_other = stage_other; - - stage_index = 0; - stage_load = false; - stage_index_load = 0; + //GetSetSize(); } DtwStg::~DtwStg() { } -bool DtwStg::Init() { - return true; +void DtwStg::Hide() { + SetDisp(false); } -bool DtwStg::Ctrl() { - if (stage_load) { - task_stage_set_stage_index(stage_index_load); - stage_load = false; - } - return false; -} - -bool DtwStg::Dest() { - return true; -} - -void DtwStg::Window() { - +/*void DtwStg::Window() { ImGuiIO& io = ImGui::GetIO(); ImGuiStyle& style = ImGui::GetStyle(); ImFont* font = ImGui::GetFont(); @@ -186,7 +197,7 @@ void DtwStg::Window() { std::vector& stg_data = aft_stage_data->stage_data; - int32_t _pv_id = pv_id; + int32_t _pv_id = this->_pv_id; char buf[0x100]; sprintf_s(buf, sizeof(buf), "%03d", _pv_id); @@ -199,8 +210,8 @@ void DtwStg::Window() { ImGui::PushID(&i); sprintf_s(buf, sizeof(buf), "%03d", i.pv_id); if (ImGui::Selectable(buf, _pv_id == i.pv_id) - || ImGui::ItemKeyPressed(GLFW_KEY_ENTER, true) - || (ImGui::IsItemFocused() && pv_id != i.pv_id)) + || ImGui::ItemKeyPressed(ImGuiKey_Enter) + || (ImGui::IsItemFocused() && _pv_id != i.pv_id)) _pv_id = i.pv_id; ImGui::PopID(); @@ -212,9 +223,9 @@ void DtwStg::Window() { ImGui::EndCombo(); } - if (_pv_id != pv_id) { + if (_pv_id != this->_pv_id) { pv_index = -1; - pv_id = _pv_id; + this->_pv_id = _pv_id; } int32_t _pv_index = pv_index; @@ -223,7 +234,7 @@ void DtwStg::Window() { ImGui::SetNextItemWidth(160.0f); bool pv_id_found = false; for (stage_test_stage_pv& i : stage_pv) { - if (pv_id != i.pv_id) + if (_pv_id != i.pv_id) continue; if (ImGui::BeginCombo("##PV Index", pv_index > -1 @@ -233,7 +244,7 @@ void DtwStg::Window() { ImGui::PushID(j); if (ImGui::Selectable(stg_data[j].name.c_str(), _pv_index == pv_idx) - || ImGui::ItemKeyPressed(GLFW_KEY_ENTER, true) + || ImGui::ItemKeyPressed(ImGuiKey_Enter) || (ImGui::IsItemFocused() && _pv_index != pv_idx)) { pv_index = -1; _pv_index = pv_idx; @@ -248,8 +259,8 @@ void DtwStg::Window() { } if (_pv_index != pv_index) { - stage_load = true; - stage_index_load = i.stage[_pv_index]; + //stage_load = true; + //stage_index_load = i.stage[_pv_index]; pv_index = _pv_index; } pv_id_found = true; @@ -273,7 +284,7 @@ void DtwStg::Window() { ImGui::PushID(i); if (ImGui::Selectable(stg_data[stage_ns[i]].name.c_str(), _ns_index == ns_idx) - || ImGui::ItemKeyPressed(GLFW_KEY_ENTER, true) + || ImGui::ItemKeyPressed(ImGuiKey_Enter) || (ImGui::IsItemFocused() && _ns_index != ns_idx)) { ns_index = -1; _ns_index = ns_idx; @@ -289,8 +300,8 @@ void DtwStg::Window() { } if (_ns_index != ns_index) { - stage_load = true; - stage_index_load = stage_ns[_ns_index]; + //stage_load = true; + //stage_index_load = stage_ns[_ns_index]; ns_index = _ns_index; } @@ -306,7 +317,7 @@ void DtwStg::Window() { ImGui::PushID(i); if (ImGui::Selectable(stg_data[i].name.c_str(), _other_index == other_idx) - || ImGui::ItemKeyPressed(GLFW_KEY_ENTER, true) + || ImGui::ItemKeyPressed(ImGuiKey_Enter) || (ImGui::IsItemFocused() && _other_index != other_idx)) { other_index = -1; _other_index = other_idx; @@ -322,8 +333,8 @@ void DtwStg::Window() { } if (_other_index != other_index) { - stage_load = true; - stage_index_load = stage_other[_other_index]; + //stage_load = true; + //stage_index_load = stage_other[_other_index]; other_index = _other_index; } @@ -337,7 +348,7 @@ void DtwStg::Window() { ImGui::PushID(&i); if (ImGui::Selectable(i.name.c_str(), _stage_index == stage_idx) - || ImGui::ItemKeyPressed(GLFW_KEY_ENTER, true) + || ImGui::ItemKeyPressed(ImGuiKey_Enter) || (ImGui::IsItemFocused() && _stage_index != stage_idx)) { stage_index = -1; _stage_index = stage_idx; @@ -353,19 +364,20 @@ void DtwStg::Window() { } if (_stage_index != stage_index) { - stage_load = true; - stage_index_load = _stage_index; + //stage_load = true; + //stage_index_load = _stage_index; stage_index = _stage_index; } - stage* stg = task_stage_get_current_stage(); + ::stage* stg = task_stage_get_current_stage(); if (stg) { ImGui::Checkbox("Stage display", &stg->stage_display); ImGui::Checkbox("[Ring]", &stg->ring); ImGui::Checkbox("[Ground]", &stg->ground); ImGui::Checkbox("[Sky]", &stg->sky); - ImGui::Checkbox("Effects display", &stg->effect_display); - + //ImGui::Checkbox("Effects display", &stg->effect_display); + bool effects_display = true; + ImGui::Checkbox("Effects display", &effects_display); } else { bool stage_display = true; @@ -382,19 +394,73 @@ void DtwStg::Window() { window_focus |= ImGui::IsWindowFocused(); ImGui::End(); +}*/ + +void DtwStg::PvIdCallback(dw::Widget* data) { + dw::ListBox* list_box = dynamic_cast(data); + if (list_box) { + data_struct* aft_data = &data_list[DATA_AFT]; + stage_database* aft_stage_data = &aft_data->data_ft.stage_data; + + std::string name = list_box->GetItem(list_box->list->selected_item); + + dw::ListBox* pv_list_box = dtw_stg->pv; + pv_list_box->ClearItems(); + auto elem = dtw_stg->pv_stage.find(name); + if (elem != dtw_stg->pv_stage.end()) + for (std::string& i : elem->second) + pv_list_box->AddItem(i); + } +} + +void DtwStg::StageCallback(dw::Widget* data) { + dw::ListBox* list_box = dynamic_cast(data); + if (list_box) { + data_struct* aft_data = &data_list[DATA_AFT]; + stage_database* aft_stage_data = &aft_data->data_ft.stage_data; + + std::string name = list_box->GetItem(list_box->list->selected_item); + dtm_stg->load_stage_index = aft_stage_data->get_stage_index(name.c_str()); + } +} + +void dtm_stg_init() { + dtm_stg = new DtmStg; +} + +void dtm_stg_load(int32_t stage_index) { + if (app::TaskWork::CheckTaskReady(dtm_stg)) + return; + + if (app::TaskWork::CheckTaskReady(dtm_stg)) { + dtm_stg->stage_index = stage_index; + dtm_stg->load_stage_index = stage_index; + } + app::TaskWork::AddTask(dtm_stg, "DATA_TEST_STAGE"); +} + +bool dtm_stg_unload() { + return dtm_stg->DelTask(); +} + +void dtm_stg_free() { + if (dtm_stg) { + delete dtm_stg; + dtm_stg = 0; + } } void dtw_stg_init() { dtw_stg = new DtwStg; + dtw_stg->Disp(); } void dtw_stg_load(bool hide) { - if (hide) - dtw_stg->HideWindow(); + dtw_stg->SetDisp(!hide); } void dtw_stg_unload() { - + dtw_stg->Hide(); } void dtw_stg_free() { diff --git a/src/ReDIVA/data_test/stage_test.hpp b/src/ReDIVA/data_test/stage_test.hpp index 55f705a2..da201169 100644 --- a/src/ReDIVA/data_test/stage_test.hpp +++ b/src/ReDIVA/data_test/stage_test.hpp @@ -6,6 +6,7 @@ #pragma once #include "../../KKdLib/default.hpp" +#include "../dw.hpp" #include "../task_window.hpp" struct stage_test_stage_pv { @@ -16,32 +17,51 @@ struct stage_test_stage_pv { ~stage_test_stage_pv(); }; -class DtwStg : public app::TaskWindow { +class DtmStg : public app::Task { public: - int32_t pv_id; - int32_t pv_index; - int32_t ns_index; - int32_t other_index; int32_t stage_index; + int32_t load_stage_index; - int32_t stage_index_load; - bool stage_load; - - std::vector stage_pv; - std::vector stage_ns; - std::vector stage_other; - - DtwStg(); - virtual ~DtwStg() override; + DtmStg(); + virtual ~DtmStg() override; virtual bool Init() override; virtual bool Ctrl() override; virtual bool Dest() override; - virtual void Window() override; }; +class DtwStg : public dw::Shell { +public: + std::map> pv_stage; + dw::ListBox* stage; + dw::ListBox* ns; + dw::ListBox* pv; + dw::ListBox* pv_id; + dw::ListBox* other; + dw::Button* stage_display; + dw::Button* ground; + dw::Button* ring; + dw::Button* sky; + dw::Button* effect_display; + + DtwStg(); + virtual ~DtwStg() override; + + virtual void Hide() override; + + static void PvIdCallback(dw::Widget* data); + static void StageCallback(dw::Widget* data); +}; + +extern DtmStg* dtm_stg; + extern DtwStg* dtw_stg; +extern void dtm_stg_init(); +extern void dtm_stg_load(int32_t stage_index); +extern bool dtm_stg_unload(); +extern void dtm_stg_free(); + extern void dtw_stg_init(); extern void dtw_stg_load(bool hide); extern void dtw_stg_unload(); diff --git a/src/ReDIVA/dw.cpp b/src/ReDIVA/dw.cpp new file mode 100644 index 00000000..d57951da --- /dev/null +++ b/src/ReDIVA/dw.cpp @@ -0,0 +1,386 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#include "dw.hpp" +#include "imgui_helper.hpp" + +namespace dw { + SelectionListener::SelectionListener() { + + } + + SelectionListener::~SelectionListener() { + + } + + SelectionAdapter::SelectionAdapter() { + + } + + SelectionAdapter::~SelectionAdapter() { + + } + + SelectionListenerOnHook::SelectionListenerOnHook() : callback() { + + } + + SelectionListenerOnHook::SelectionListenerOnHook(Widget::Callback callback) { + this->callback = callback; + } + + SelectionListenerOnHook::~SelectionListenerOnHook() { + + } + + Widget::Widget(Widget* parent, Flags flags) : callback_data(), parent() { + this->flags = flags; + } + + Widget::~Widget() { + + } + + void Widget::Draw() { + + } + + void Widget::Reset() { + name.clear(); + } + + std::string Widget::GetName() { + return name; + } + + void Widget::SetName(std::string& str) { + name.assign(str); + } + + void Widget::SetSize(vec2 value) { + size = value; + } + + vec2 Widget::GetPos() { + return position; + } + + void Widget::SetName(std::string&& str) { + SetName(str); + } + + Control::Control(Composite* parent, Flags flags) : Widget(parent, flags), + parent_comp(), parent_shell()/*, parent_menu()*/{ + parent_comp = parent; + if (parent) { + parent->controls.push_back(this); + parent_shell = parent->parent_shell; + } + } + + Control::~Control() { + + } + + void Control::Draw() { + + } + + void Control::Reset() { + parent_comp = 0; + Widget::Reset(); + } + + Label::Label(Composite* parent, Flags flags) : Control(parent, flags) { + + } + + Label::~Label() { + + } + + void Label::Draw() { + ImGui::Text(name.c_str()); + } + + Scrollable::Scrollable(Composite* parent, Flags flags) : Control(parent, flags) { + + } + + Scrollable::~Scrollable() { + + } + + void Scrollable::Draw() { + + } + + void Scrollable::Reset() { + Control::Reset(); + } + + Layout::Layout() { + + } + + Layout::~Layout() { + + } + + FillLayout::FillLayout(int32_t a2) { + field_8 = a2; + } + + FillLayout::~FillLayout() { + + } + + vec2 FillLayout::GetSize(dw::Composite* comp) { + return comp->size; + } + + void FillLayout::SetSize(dw::Composite* comp) { + const vec2 position = comp->position; + const vec2 size = comp->size; + for (Control* i : comp->controls) { + i->position = position; + i->SetSize(size); + } + } + + GraphLayout::GraphLayout(int32_t a2) { + field_8 = a2; + } + + GraphLayout::~GraphLayout() { + + } + + vec2 GraphLayout::GetSize(dw::Composite* comp) { + return comp->size; + } + + void GraphLayout::SetSize(dw::Composite* comp) { + const vec2 position = comp->position; + const vec2 size = comp->size; + for (Control* i : comp->controls) { + i->position = position; + i->SetSize(size); + } + } + + GridLayout::GridLayout(size_t a2) : field_8(), field_10(), + field_18(), field_20(), field_28(), field_30(), field_38() { + field_40 = a2; + field_48 = 2.0f; + field_4C = 2.0f; + } + + GridLayout::~GridLayout() { + + } + + vec2 GridLayout::GetSize(dw::Composite* comp) { + return comp->size; + } + + void GridLayout::SetSize(dw::Composite* comp) { + const vec2 position = comp->position; + const vec2 size = comp->size; + for (Control* i : comp->controls) { + i->position = position; + i->SetSize(size); + } + } + + RowLayout::RowLayout(int32_t a2) { + field_8 = 512; + field_C = 2.0f; + } + + RowLayout::~RowLayout() { + + } + + vec2 RowLayout::GetSize(dw::Composite* comp) { + return comp->size; + } + + void RowLayout::SetSize(dw::Composite* comp) { + const vec2 position = comp->position; + const vec2 size = comp->size; + for (Control* i : comp->controls) { + i->position = position; + i->SetSize(size); + } + } + + Composite::Composite(Composite* parent, Flags flags) : Scrollable(parent, flags), layout() { + + } + + Composite::~Composite() { + + } + + void Composite::Draw() { + for (Control* i : controls) + i->Draw(); + } + + void Composite::Reset() { + //for (Control* i : controls) + //dw::Widget::sub_1402ECDE0(i); + + controls.clear(); + Scrollable::Reset(); + } + + void Composite::SetLayout(dw::Layout* value) { + layout = value; + } + + Button::Button(Composite* parent, Flags flags) : Control(parent, flags), value(), callback() { + + } + + Button::~Button() { + + } + + void Button::Draw() { + if (flags & WIDGET_CHECKBOX) + ImGui::Checkbox(name.c_str(), &value); + else if (flags & WIDGET_RADIOBUTTON) { + if (ImGui::RadioButton(name.c_str(), value)) { + if (callback) + callback(this); + value = true; + } + } + else + ImGui::Button(name.c_str()); + } + + void Button::SetValue(bool value) { + this->value = value; + if (!(flags & WIDGET_RADIOBUTTON)) + return; + + for (Control* i : parent_comp->controls) { + Button* button = (Button*)dynamic_cast(i); + if (button && button != this && button->flags & WIDGET_RADIOBUTTON) + button->value = false; + } + } + + Shell::Shell(Composite* parent, Flags flags) : Composite(parent, flags), disp() { + + } + + Shell::~Shell() { + + } + + void Shell::Draw() { + if (!GetDisp()) + return; + + Composite::Draw(); + } + + void Shell::Hide() { + + } + + void Shell::Disp() { + SetDisp(true); + } + + bool Shell::GetDisp() { + return disp; + } + + void Shell::SetDisp(bool value) { + if (disp == value) + return; + + disp = value; + } + + List::List(Composite* parent, Flags flags) : Scrollable(parent, flags) { + hovered_item = -1; + selected_item = -1; + } + + List::~List() { + + } + + void List::Draw() { + + } + + void List::Reset() { + items.clear(); + Scrollable::Reset(); + } + + void List::AddItem(const std::string& str) { + items.push_back(str); + } + + void List::AddItem(const std::string&& str) { + items.push_back(str); + } + + void List::AddSelectionListener(SelectionListener* value) { + selection_listeners.push_back(value); + } + + void List::ClearItems() { + items.clear(); + } + + std::string List::GetItem(size_t index) const { + return items[index]; + } + + ListBox::ListBox(Composite* parent, Flags flags) : Composite(parent, flags), list() { + list = new List(parent, flags); + } + + ListBox::~ListBox() { + delete list; + } + + void ListBox::Draw() { + + } + + void ListBox::Reset() { + Composite::Reset(); + } + + void ListBox::AddItem(const std::string& str) { + list->AddItem(str); + } + + void ListBox::AddItem(const std::string&& str) { + list->AddItem(str); + } + + void ListBox::AddSelectionListener(SelectionListener* value) { + list->AddSelectionListener(value); + } + + void ListBox::ClearItems() { + list->ClearItems(); + } + + std::string ListBox::GetItem(size_t index) const { + return list->GetItem(index); + } +} diff --git a/src/ReDIVA/dw.hpp b/src/ReDIVA/dw.hpp new file mode 100644 index 00000000..de9901c4 --- /dev/null +++ b/src/ReDIVA/dw.hpp @@ -0,0 +1,252 @@ +/* + by korenkonder + GitHub/GitLab: korenkonder +*/ + +#pragma once + +#include "../KKdLib/default.hpp" +#include "../KKdLib/vec.hpp" +#include + +namespace dw { + class Widget { + public: + typedef void(*Callback)(Widget* data); + + enum Flags { + WIDGET_RADIOBUTTON = 0x10, + WIDGET_CHECKBOX = 0x20, + WIDGET_CLOSE_BUTTON = 0x40, + }; + + union { + int8_t i8; + uint8_t u8; + int16_t i16; + uint16_t u16; + int32_t i32; + uint32_t u32; + int64_t i64; + uint64_t u64; + void* v64; + } callback_data; + + std::string name; + vec2 position; + vec2 size; + Widget* parent; + Flags flags; + + Widget(Widget* parent = 0, Flags flags = (Flags)0); + virtual ~Widget(); + + virtual void Draw(); + virtual void Reset(); + virtual std::string GetName(); + virtual void SetName(std::string& str); + virtual void SetSize(vec2 value); + virtual vec2 GetPos(); + + void SetName(std::string&& str); + }; + + class SelectionListener { + public: + SelectionListener(); + virtual ~SelectionListener(); + }; + + class SelectionAdapter : public SelectionListener { + public: + SelectionAdapter(); + virtual ~SelectionAdapter() override; + }; + + class SelectionListenerOnHook : public SelectionAdapter { + public: + Widget::Callback callback; + + SelectionListenerOnHook(); + SelectionListenerOnHook(Widget::Callback callback); + virtual ~SelectionListenerOnHook() override; + }; + + class Composite; + class Shell; + + class Control : public Widget { + public: + Composite* parent_comp; + Shell* parent_shell; + //Menu* parent_menu; + + Control(Composite* parent = 0, Flags flags = (Flags)0); + virtual ~Control() override; + + virtual void Draw() override; + virtual void Reset() override; + }; + + class Label : public Control { + public: + Label(Composite* parent = 0, Flags flags = (Flags)0); + virtual ~Label() override; + + virtual void Draw() override; + }; + + class Scrollable : public Control { + public: + Scrollable(Composite* parent = 0, Flags flags = (Flags)0); + virtual ~Scrollable() override; + + virtual void Draw() override; + virtual void Reset() override; + }; + + class Layout { + public: + Layout(); + virtual ~Layout(); + + virtual vec2 GetSize(dw::Composite* comp) = 0; + virtual void SetSize(dw::Composite* comp) = 0; + }; + + class FillLayout : public Layout { + public: + int32_t field_8; + + FillLayout(int32_t a2 = 512); + virtual ~FillLayout() override; + + virtual vec2 GetSize(dw::Composite* comp) override; + virtual void SetSize(dw::Composite* comp) override; + }; + + class GraphLayout : public Layout { + public: + int32_t field_8; + + GraphLayout(int32_t a2 = 512); + virtual ~GraphLayout() override; + + virtual vec2 GetSize(dw::Composite* comp) override; + virtual void SetSize(dw::Composite* comp) override; + }; + + class GridLayout : public Layout { + public: + __int64 field_8; + __int64 field_10; + __int64 field_18; + __int64 field_20; + __int64 field_28; + __int64 field_30; + __int64 field_38; + size_t field_40; + float_t field_48; + float_t field_4C; + + GridLayout(size_t a2 = 2); + virtual ~GridLayout() override; + + virtual vec2 GetSize(dw::Composite* comp) override; + virtual void SetSize(dw::Composite* comp) override; + }; + + class RowLayout : public Layout { + public: + int32_t field_8; + float_t field_C; + + RowLayout(int32_t a2 = 256); + virtual ~RowLayout() override; + + virtual vec2 GetSize(dw::Composite* comp) override; + virtual void SetSize(dw::Composite* comp) override; + }; + + class Composite : public Scrollable { + public: + dw::Layout* layout; + std::vector controls; + + Composite(Composite* parent = 0, Flags flags = (Flags)0); + virtual ~Composite() override; + + virtual void Draw() override; + virtual void Reset() override; + + void SetLayout(dw::Layout* value); + }; + + class Button : public Control { + public: + bool value; + std::vector selection_listeners; + Widget::Callback callback; + + Button(Composite* parent = 0, Flags flags = (Flags)0); + virtual ~Button() override; + + virtual void Draw() override; + + void SetValue(bool value); + }; + + class Shell : public Composite { + public: + bool disp; + + Shell(Composite* parent = 0, Flags flags + = (dw::Widget::Flags)(0x480 | WIDGET_CLOSE_BUTTON | WIDGET_CHECKBOX | WIDGET_RADIOBUTTON)); + virtual ~Shell() override; + + virtual void Draw() override; + + virtual void Hide(); + + void Disp(); + bool GetDisp(); + void SetDisp(bool value); + }; + + class List : public Scrollable { + public: + std::vector items; + size_t hovered_item; + size_t selected_item; + std::vector selection_listeners; + + List(Composite* parent = 0, Flags flags = (Flags)0); + virtual ~List() override; + + virtual void Draw() override; + virtual void Reset() override; + + void AddItem(const std::string& str); + void AddItem(const std::string&& str); + void AddSelectionListener(SelectionListener* value); + void ClearItems(); + std::string GetItem(size_t index) const; + }; + + class ListBox : public Composite { + public: + dw::List* list; + + ListBox(Composite* parent = 0, Flags flags = (Flags)0x204); + virtual ~ListBox() override; + + virtual void Draw() override; + virtual void Reset() override; + + void AddItem(const std::string& str); + void AddItem(const std::string&& str); + void AddSelectionListener(SelectionListener* value); + void ClearItems(); + std::string GetItem(size_t index) const; + }; +} diff --git a/src/ReDIVA/game_state.cpp b/src/ReDIVA/game_state.cpp index 36be71d3..7b02ecc1 100644 --- a/src/ReDIVA/game_state.cpp +++ b/src/ReDIVA/game_state.cpp @@ -10,6 +10,7 @@ #include "../CRE/task.hpp" #include "data_test/auth_3d_test.hpp" #include "data_test/glitter_test.hpp" +#include "data_test/motion_test.hpp" #include "data_test/stage_test.hpp" #include "data_edit.hpp" #include "data_initialize.hpp" @@ -17,6 +18,331 @@ #include "system_startup.hpp" #include "x_pv_game.hpp" +enum GameStateMode { + GAME_STATE_MODE_INIT = 0, + GAME_STATE_MODE_CTRL, + GAME_STATE_MODE_DEST, + GAME_STATE_MODE_MAX, +}; + +struct GameState { + struct Startup { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct Advertise { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct Game { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct DataTest { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct TestMode { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + +#if DATA_EDIT + struct DataEdit { // Added + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; +#endif + + struct AppError { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + GameStateEnum game_state; + GameStateEnum game_state_next; + GameStateEnum game_state_prev; + GameStateMode mode; + bool set_game_state_next; + bool next_sub_game_state; + int32_t call_count; + int32_t advertise_state; + SubGameStateEnum sub_game_state; + SubGameStateEnum sub_game_state_next; + SubGameStateEnum sub_game_state_prev; + GameStateMode sub_mode; + bool set_sub_game_state_next; + bool next_game_state; + int32_t sub_call_count; +}; + +struct SubGameState { + struct DataInitialize { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct SystemStartup { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct SystemStartupError { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct Warning { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct Logo { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct Rating { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct Demo { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct Title { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct Ranking { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct ScoreRanking { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct CM { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct PhotoModeDemo { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct Selector { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct GameMain { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct GameSel { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct StageResult { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct ScreenShotSel { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct ScreenShotResult { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct GameOver { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct DataTestMain { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct DataTestMisc { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct DataTestObj { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct DataTestStg { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct DataTestMot { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct DataTestCollision { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct DataTestSpr { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct DataTestAet { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct DataTestAuth3d { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct DataTestChr { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct DataTestItem { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct DataTestPerf { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct DataTestPvScript { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct DataTestPrint { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct DataTestCard { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct DataTestOpd { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct DataTestSlider { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct DataTestGlitter { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct DataTestGraphics { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct DataTestCollectionCard { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + + struct DataTestModeMain { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; + +#if DATA_EDIT + struct DataEdit { // Added + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; +#endif + + struct DataTestAppError { + static bool Init(); + static bool Ctrl(); + static bool Dest(); + }; +}; + struct GameStateData { GameStateEnum game_state; bool(*Init)(); @@ -530,6 +856,7 @@ bool test_mode; GameStateEnum data_test_game_state_prev; static bool game_state_call_sub(GameState* game_state); +static GameState* game_state_get(); static void game_state_set_state(GameStateEnum state, SubGameStateEnum sub_state); static GameStateData* game_state_data_array_get(GameStateEnum state); @@ -569,7 +896,7 @@ bool GameState::Advertise::Dest() { } bool GameState::Game::Init() { - task_rob_manager_append_task(); + task_rob_manager_add_task(); return true; } @@ -578,14 +905,14 @@ bool GameState::Game::Ctrl() { } bool GameState::Game::Dest() { - if (!task_pv_game_free_task() || !task_rob_manager_free_task()) + if (!task_pv_game_del_task() || !task_rob_manager_del_task()) return false; return true; } bool GameState::DataTest::Init() { - task_rob_manager_append_task(); + task_rob_manager_add_task(); return true; } @@ -600,7 +927,7 @@ bool GameState::DataTest::Ctrl() { } bool GameState::DataTest::Dest() { - task_rob_manager_free_task(); + task_rob_manager_del_task(); return true; } @@ -645,9 +972,10 @@ bool GameState::AppError::Dest() { } bool SubGameState::DataInitialize::Init() { - task_data_init_append_task(); - task_auth_3d_append_task(); - //task_auth_2d_append_task(); + task_data_init_add_task(); + task_auth_3d_add_task(); + //task_auth_2d_add_task(); + //no_good_add_task(); return true; } @@ -656,13 +984,13 @@ bool SubGameState::DataInitialize::Ctrl() { } bool SubGameState::DataInitialize::Dest() { - task_data_init_free_task(); + task_data_init_del_task(); return true; } bool SubGameState::SystemStartup::Init() { - task_system_startup_append_task(); - task_pv_db_append_task(); + task_system_startup_add_task(); + task_pv_db_add_task(); return true; } @@ -671,7 +999,7 @@ bool SubGameState::SystemStartup::Ctrl() { } bool SubGameState::SystemStartup::Dest() { - task_system_startup_free_task(); + task_system_startup_del_task(); game_state_set_sub_game_state_next(SUB_GAME_STATE_WARNING); return true; } @@ -806,7 +1134,7 @@ bool SubGameState::PhotoModeDemo::Dest() { } bool SubGameState::Selector::Init() { - app::TaskWork::AppendTask(&x_pv_game_selector, "X PVGAME SELECTOR", 0); + app::TaskWork::AddTask(&x_pv_game_selector, "X PVGAME SELECTOR", 0); return true; } @@ -814,7 +1142,7 @@ bool SubGameState::Selector::Ctrl() { if (x_pv_game_selector.exit) { if (x_pv_game_selector.start && x_pv_game_init()) { XPVGameSelector& sel = x_pv_game_selector; - app::TaskWork::AppendTask(x_pv_game_ptr, "X PVGAME", 0); + app::TaskWork::AddTask(x_pv_game_ptr, "X PVGAME", 0); x_pv_game_ptr->Load(sel.pv_id, sel.stage_id, sel.charas, sel.modules); game_state_set_sub_game_state_next(SUB_GAME_STATE_GAME_MAIN); } @@ -826,7 +1154,7 @@ bool SubGameState::Selector::Ctrl() { } bool SubGameState::Selector::Dest() { - x_pv_game_selector.SetDest(); + x_pv_game_selector.DelTask(); return true; } @@ -953,9 +1281,8 @@ bool SubGameState::DataTestStg::Init() { cam->set_view_point({ 0.0f, 0.88f, 4.3f }); cam->set_interest({ 0.0f, 1.0f, 0.0f }); - app::TaskWork::AppendTask(dtm_stg, "DATA_TEST_STAGE"); - app::TaskWork::AppendTask(dtw_stg, "DATA_TEST_STAGE WINDOW"); - dtw_stg->ShowWindow(); + app::TaskWork::AddTask(dtm_stg, "DATA_TEST_STAGE"); + dtw_stg_init(); return true; } @@ -966,20 +1293,26 @@ bool SubGameState::DataTestStg::Ctrl() { bool SubGameState::DataTestStg::Dest() { clear_color = { 0x00, 0x00, 0x00, 0xFF }; - dtm_stg->SetDest(); - dtw_stg->SetDest(); + dtw_stg_free(); + dtm_stg->DelTask(); return true; } bool SubGameState::DataTestMot::Init() { + app::TaskWork::AddTask(data_test_mot, "TASK_DATA_TEST_MOT"); + dtm_stg_load(0); + dtw_stg_load(true); return true; } bool SubGameState::DataTestMot::Ctrl() { - return false; + return task_data_init_check_state(); } bool SubGameState::DataTestMot::Dest() { + data_test_mot->DelTask(); + dtm_stg_unload(); + dtw_stg_unload(); return true; } @@ -1020,8 +1353,8 @@ bool SubGameState::DataTestAet::Dest() { } bool SubGameState::DataTestAuth3d::Init() { - app::TaskWork::AppendTask(auth_3d_test_task, "AUTH3DTEST"); - app::TaskWork::AppendTask(auth_3d_test_window, "AUTH3DTEST WINDOW"); + app::TaskWork::AddTask(auth_3d_test_task, "AUTH3DTEST"); + app::TaskWork::AddTask(auth_3d_test_window, "AUTH3DTEST WINDOW"); return true; } @@ -1030,8 +1363,8 @@ bool SubGameState::DataTestAuth3d::Ctrl() { } bool SubGameState::DataTestAuth3d::Dest() { - auth_3d_test_task->SetDest(); - auth_3d_test_window->SetDest(); + auth_3d_test_task->DelTask(); + auth_3d_test_window->DelTask(); return true; } @@ -1132,7 +1465,7 @@ bool SubGameState::DataTestSlider::Dest() { } bool SubGameState::DataTestGlitter::Init() { - app::TaskWork::AppendTask(task_data_test_glitter_particle, "DATA_TEST_PARTICLE"); + app::TaskWork::AddTask(task_data_test_glitter_particle, "DATA_TEST_PARTICLE"); dtm_stg_load(0); dtw_stg_load(true); return true; @@ -1146,8 +1479,8 @@ bool SubGameState::DataTestGlitter::Dest() { dtw_stg_unload(); dtm_stg_unload(); //if (data_test_chr->CheckTaskReady()) - // data_test_chr->SetDest(); - task_data_test_glitter_particle->SetDest(); + // data_test_chr->DelTask(); + task_data_test_glitter_particle->DelTask(); return true; } @@ -1189,7 +1522,7 @@ bool SubGameState::DataTestModeMain::Dest() { #if DATA_EDIT bool SubGameState::DataEdit::Init() { // Added - app::TaskWork::AppendTask(&data_edit, "DATA EDIT", 0); + app::TaskWork::AddTask(&data_edit, "DATA EDIT", 0); return true; } @@ -1198,7 +1531,7 @@ bool SubGameState::DataEdit::Ctrl() { // Added } bool SubGameState::DataEdit::Dest() { // Added - data_edit.SetDest(); + data_edit.DelTask(); return true; } #endif @@ -1302,10 +1635,6 @@ void game_state_ctrl() { game_state->call_count++; } -GameState* game_state_get() { - return &game_state; -} - size_t game_state_print(char* buf, size_t buf_size) { GameState* game_state = game_state_get(); const char* game_state_name = game_state_names[game_state->game_state]; @@ -1373,6 +1702,9 @@ static bool game_state_call_sub(GameState* game_state) { return false; } +static GameState* game_state_get() { + return &game_state; +} static void game_state_set_state(GameStateEnum state, SubGameStateEnum sub_state) { GameState* game_state = game_state_get(); diff --git a/src/ReDIVA/game_state.hpp b/src/ReDIVA/game_state.hpp index 463fbefc..fd8e9e3d 100644 --- a/src/ReDIVA/game_state.hpp +++ b/src/ReDIVA/game_state.hpp @@ -69,334 +69,8 @@ enum SubGameStateEnum { SUB_GAME_STATE_MAX, }; -enum GameStateMode { - GAME_STATE_MODE_INIT = 0, - GAME_STATE_MODE_CTRL, - GAME_STATE_MODE_DEST, - GAME_STATE_MODE_MAX, -}; - -struct GameState { - struct Startup { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct Advertise { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct Game { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct DataTest { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct TestMode { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - -#if DATA_EDIT - struct DataEdit { // Added - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; -#endif - - struct AppError { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - GameStateEnum game_state; - GameStateEnum game_state_next; - GameStateEnum game_state_prev; - GameStateMode mode; - bool set_game_state_next; - bool next_sub_game_state; - int32_t call_count; - int32_t advertise_state; - SubGameStateEnum sub_game_state; - SubGameStateEnum sub_game_state_next; - SubGameStateEnum sub_game_state_prev; - GameStateMode sub_mode; - bool set_sub_game_state_next; - bool next_game_state; - int32_t sub_call_count; -}; - -struct SubGameState { - struct DataInitialize { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct SystemStartup { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct SystemStartupError { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct Warning { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct Logo { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct Rating { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct Demo { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct Title { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct Ranking { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct ScoreRanking { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct CM { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct PhotoModeDemo { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct Selector { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct GameMain { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct GameSel { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct StageResult { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct ScreenShotSel { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct ScreenShotResult { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct GameOver { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct DataTestMain { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct DataTestMisc { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct DataTestObj { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct DataTestStg { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct DataTestMot { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct DataTestCollision { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct DataTestSpr { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct DataTestAet { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct DataTestAuth3d { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct DataTestChr { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct DataTestItem { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct DataTestPerf { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct DataTestPvScript { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct DataTestPrint { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct DataTestCard { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct DataTestOpd { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct DataTestSlider { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct DataTestGlitter { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct DataTestGraphics { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct DataTestCollectionCard { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - - struct DataTestModeMain { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; - -#if DATA_EDIT - struct DataEdit { // Added - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; -#endif - - struct DataTestAppError { - static bool Init(); - static bool Ctrl(); - static bool Dest(); - }; -}; - extern void game_state_init(); extern void game_state_ctrl(); -extern GameState* game_state_get(); extern size_t game_state_print(char* buf, size_t buf_size); extern void game_state_set_game_state_next(GameStateEnum state); extern void game_state_set_sub_game_state_next(SubGameStateEnum state); diff --git a/src/ReDIVA/imgui_helper.cpp b/src/ReDIVA/imgui_helper.cpp index c449599c..5b163aff 100644 --- a/src/ReDIVA/imgui_helper.cpp +++ b/src/ReDIVA/imgui_helper.cpp @@ -99,7 +99,7 @@ namespace ImGui { ResetIsItemAccum(); ButtonEx("<", { button_size, button_size }, ImGuiButtonFlags_Repeat); GetIsItemAccum(); - bool l = IsItemActive() && (IsKeyPressed(VK_RETURN, true) + bool l = IsItemActive() && (IsKeyPressed(ImGuiKey_Enter) || IsMouseClicked(ImGuiMouseButton_Left, true)); SameLine(0.0f, 0.0f); SetNextItemWidth(w - button_size * 2.0f); @@ -108,7 +108,7 @@ namespace ImGui { SameLine(0.0f, 0.0f); ButtonEx(">", { button_size, button_size }, ImGuiButtonFlags_Repeat); GetIsItemAccum(); - bool r = IsItemActive() && (IsKeyPressed(VK_RETURN, true) + bool r = IsItemActive() && (IsKeyPressed(ImGuiKey_Enter) || IsMouseClicked(ImGuiMouseButton_Left, true)); PopButtonRepeat(); EndGroup(); @@ -153,7 +153,7 @@ namespace ImGui { ResetIsItemAccum(); ButtonEx("<", { button_size, button_size }, ImGuiButtonFlags_Repeat); GetIsItemAccum(); - bool l = IsItemActive() && (IsKeyPressed(VK_RETURN, true) + bool l = IsItemActive() && (IsKeyPressed(ImGuiKey_Enter) || IsMouseClicked(ImGuiMouseButton_Left, true)); SameLine(0.0f, 0.0f); SetNextItemWidth(w - button_size * 2.0f); @@ -163,7 +163,7 @@ namespace ImGui { SameLine(0.0f, 0.0f); ButtonEx(">", { button_size, button_size }, ImGuiButtonFlags_Repeat); GetIsItemAccum(); - bool r = IsItemActive() && (IsKeyPressed(VK_RETURN, true) + bool r = IsItemActive() && (IsKeyPressed(ImGuiKey_Enter) || IsMouseClicked(ImGuiMouseButton_Left, true)); PopButtonRepeat(); EndGroup(); @@ -192,20 +192,20 @@ namespace ImGui { ResetIsItemAccum(); bool res = Button(label, size); GetIsItemAccum(); - return res || ItemKeyPressed(GLFW_KEY_ENTER, true); + return res || ItemKeyPressed(ImGuiKey_Enter); } bool ButtonExEnterKeyPressed(const char* label, const ImVec2& size, ImGuiButtonFlags flags) { ResetIsItemAccum(); bool res = ButtonEx(label, size, flags); GetIsItemAccum(); - return res || ItemKeyPressed(GLFW_KEY_ENTER, true); + return res || ItemKeyPressed(ImGuiKey_Enter); } bool CheckboxEnterKeyPressed(const char* label, bool* v) { if (Checkbox(label, v)) return true; - else if (ItemKeyPressed(GLFW_KEY_ENTER, true)) { + else if (ItemKeyPressed(ImGuiKey_Enter)) { v[0] ^= true; return true; } @@ -216,7 +216,7 @@ namespace ImGui { bool CheckboxFlagsEnterKeyPressed(const char* label, int* flags, int flags_value) { if (CheckboxFlags(label, flags, flags_value)) return true; - else if (ItemKeyPressed(GLFW_KEY_ENTER, true)) { + else if (ItemKeyPressed(ImGuiKey_Enter)) { *flags ^= flags_value; return true; } @@ -227,7 +227,7 @@ namespace ImGui { bool CheckboxFlagsEnterKeyPressed(const char* label, unsigned int* flags, unsigned int flags_value) { if (CheckboxFlags(label, flags, flags_value)) return true; - else if (ItemKeyPressed(GLFW_KEY_ENTER, true)) { + else if (ItemKeyPressed(ImGuiKey_Enter)) { *flags ^= flags_value; return true; } @@ -345,7 +345,7 @@ namespace ImGui { for (size_t n = 0; n <= size; n++) { PushID((int32_t)n); if (Selectable(items[n], *selected_idx == n) - || ItemKeyPressed(GLFW_KEY_ENTER, true) + || ItemKeyPressed(ImGuiKey_Enter) || (IsItemFocused() && *selected_idx != n)) *selected_idx = (int32_t)n; PopID(); @@ -357,7 +357,7 @@ namespace ImGui { for (size_t n = 0; n < size; n++) { PushID((int32_t)n); if (Selectable(items[n], *selected_idx == n) - || ItemKeyPressed(GLFW_KEY_ENTER, true) + || ItemKeyPressed(ImGuiKey_Enter) || (IsItemFocused() && *selected_idx != n)) *selected_idx = (int32_t)n; PopID(); @@ -393,7 +393,7 @@ namespace ImGui { for (size_t n = 0; n <= size; n++) { PushID((void*)n); if (Selectable(items[n], *selected_idx == n) - || ItemKeyPressed(GLFW_KEY_ENTER, true) + || ItemKeyPressed(ImGuiKey_Enter) || (IsItemFocused() && *selected_idx != n)) *selected_idx = n; PopID(); @@ -405,7 +405,7 @@ namespace ImGui { for (size_t n = 0; n < size; n++) { PushID((void*)n); if (Selectable(items[n], *selected_idx == n) - || ItemKeyPressed(GLFW_KEY_ENTER, true) + || ItemKeyPressed(ImGuiKey_Enter) || (IsItemFocused() && *selected_idx != n)) *selected_idx = n; PopID(); @@ -441,7 +441,7 @@ namespace ImGui { for (size_t n = 0; n <= size; n++) { PushID((int32_t)n); if (Selectable(items[n].c_str(), *selected_idx == n) - || ItemKeyPressed(GLFW_KEY_ENTER, true) + || ItemKeyPressed(ImGuiKey_Enter) || (IsItemFocused() && *selected_idx != n)) *selected_idx = (int32_t)n; PopID(); @@ -453,7 +453,7 @@ namespace ImGui { for (size_t n = 0; n < size; n++) { PushID((int32_t)n); if (Selectable(items[n].c_str(), *selected_idx == n) - || ItemKeyPressed(GLFW_KEY_ENTER, true) + || ItemKeyPressed(ImGuiKey_Enter) || (IsItemFocused() && *selected_idx != n)) *selected_idx = (int32_t)n; PopID(); @@ -489,7 +489,7 @@ namespace ImGui { for (size_t n = 0; n <= size; n++) { PushID((void*)n); if (Selectable(items[n].c_str(), *selected_idx == n) - || ItemKeyPressed(GLFW_KEY_ENTER, true) + || ItemKeyPressed(ImGuiKey_Enter) || (IsItemFocused() && *selected_idx != n)) *selected_idx = n; PopID(); @@ -501,7 +501,7 @@ namespace ImGui { for (size_t n = 0; n < size; n++) { PushID((void*)n); if (Selectable(items[n].c_str(), *selected_idx == n) - || ItemKeyPressed(GLFW_KEY_ENTER, true) + || ItemKeyPressed(ImGuiKey_Enter) || (IsItemFocused() && *selected_idx != n)) *selected_idx = n; PopID(); @@ -538,7 +538,7 @@ namespace ImGui { for (size_t n = 0; n <= size; n++) { PushID((int32_t)n); if (Selectable(items_ds[n].name.c_str(), *selected_idx == n) - || ItemKeyPressed(GLFW_KEY_ENTER, true) + || ItemKeyPressed(ImGuiKey_Enter) || (IsItemFocused() && *selected_idx != n)) *selected_idx = (int32_t)n; PopID(); @@ -550,7 +550,7 @@ namespace ImGui { for (size_t n = 0; n < size; n++) { PushID((int32_t)n); if (Selectable(items_ds[n].name.c_str(), *selected_idx == n) - || ItemKeyPressed(GLFW_KEY_ENTER, true) + || ItemKeyPressed(ImGuiKey_Enter) || (IsItemFocused() && *selected_idx != n)) *selected_idx = (int32_t)n; PopID(); @@ -587,7 +587,7 @@ namespace ImGui { for (size_t n = 0; n <= size; n++) { PushID((void*)n); if (Selectable(items_ds[n].name.c_str(), *selected_idx == n) - || ItemKeyPressed(GLFW_KEY_ENTER, true) + || ItemKeyPressed(ImGuiKey_Enter) || (IsItemFocused() && *selected_idx != n)) *selected_idx = n; PopID(); @@ -599,7 +599,7 @@ namespace ImGui { for (size_t n = 0; n < size; n++) { PushID((void*)n); if (Selectable(items_ds[n].name.c_str(), *selected_idx == n) - || ItemKeyPressed(GLFW_KEY_ENTER, true) + || ItemKeyPressed(ImGuiKey_Enter) || (IsItemFocused() && *selected_idx != n)) *selected_idx = n; PopID(); @@ -1939,11 +1939,11 @@ namespace ImGui { // Calculate bounds const float_t grab_padding = 2.0f; // FIXME: Should be part of style. const float_t slider_sz = (bb.Max[axis] - bb.Min[axis]) - grab_padding * 2.0f; - float_t grab_sz = style.GrabMinSize; + float_t grab_sz = style.GrabMinSize * 1.5f; // v_range < 0 may happen on integer overflows if (!is_floating_point && v_range >= 0) // For integer sliders: if possible have the grab size represent 1 unit - grab_sz = ImMax((float_t)(slider_sz / (v_range + 1)), style.GrabMinSize); + grab_sz = ImMax((float_t)(slider_sz / (v_range + 1)), style.GrabMinSize * 1.5f); grab_sz = ImMin(grab_sz, slider_sz); const float_t slider_usable_sz = slider_sz - grab_sz; const float_t slider_usable_pos_min = bb.Min[axis] + grab_padding + grab_sz * 0.5f; @@ -1960,23 +1960,6 @@ namespace ImGui { zero_deadzone_halfsize = (style.LogSliderDeadzone * 0.5f) / ImMax(slider_usable_sz, 1.0f); } - if (slider_sz < 1.0f) - *out_grab_bb = ImRect(bb.Min, bb.Min); - else { - // Output grab position so it can be displayed by the caller - float_t grab_t = ScaleRatioFromValueT(data_type, - *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); - if (axis == ImGuiAxis_Y) - grab_t = 1.0f - grab_t; - const float_t grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); - if (axis == ImGuiAxis_X) - *out_grab_bb = ImRect(grab_pos - grab_sz * 0.5f, bb.Min.y + grab_padding, - grab_pos + grab_sz * 0.5f, bb.Max.y - grab_padding); - else - *out_grab_bb = ImRect(bb.Min.x + grab_padding, grab_pos - grab_sz * 0.5f, - bb.Max.x - grab_padding, grab_pos + grab_sz * 0.5f); - } - // Process interacting with the slider bool value_changed = false; bool continuos_value_change = false; @@ -2001,20 +1984,31 @@ namespace ImGui { && is_floating_point) ? mouse_abs_pos - grab_pos : 0.0f; } + ImRect out_grab_bb; + if (slider_sz < 1.0f) { + out_grab_bb = ImRect(bb.Min, bb.Min); + } + else { + float_t grab_t = ScaleRatioFromValueT(data_type, + *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); + if (axis == ImGuiAxis_Y) + grab_t = 1.0f - grab_t; + const float_t grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); + if (axis == ImGuiAxis_X) + out_grab_bb = ImRect(grab_pos - grab_sz * 0.5f, bb.Min.y + grab_padding, grab_pos + grab_sz * 0.5f, bb.Max.y - grab_padding); + else + out_grab_bb = ImRect(bb.Min.x + grab_padding, grab_pos - grab_sz * 0.5f, bb.Max.x - grab_padding, grab_pos + grab_sz * 0.5f); + } + if (slider_usable_sz > 0.0f) clicked_t = ImSaturate((mouse_abs_pos - g.SliderGrabClickOffset - slider_usable_pos_min) / slider_usable_sz); if (axis == ImGuiAxis_Y) clicked_t = 1.0f - clicked_t; - - float_t diff1 = g.IO.MousePosPrev[axis] - out_grab_bb->Min[axis]; - float_t diff2 = out_grab_bb->Max[axis] - g.IO.MousePosPrev[axis]; - if (diff1 >= -grab_sz && diff2 >= -grab_sz) { - set_new_value = true; + set_new_value = true; + if (out_grab_bb.Min[axis] <= g.IO.MousePosPrev[axis] + && g.IO.MousePosPrev[axis] <= out_grab_bb.Max[axis]) continuos_value_change = true; - } - else - set_new_value = IsMouseClicked(ImGuiMouseButton_Left, true); } } else if (g.ActiveIdSource == ImGuiInputSource_Nav) { @@ -2023,24 +2017,27 @@ namespace ImGui { g.SliderCurrentAccumDirty = false; } - const ImVec2 input_delta2 = GetNavInputAmount2d(ImGuiNavDirSourceFlags_Keyboard - | ImGuiNavDirSourceFlags_PadDPad, ImGuiNavReadMode_RepeatFast, 0.0f, 0.0f); - float_t input_delta = (axis == ImGuiAxis_X) ? input_delta2.x : -input_delta2.y; + float_t input_delta = (axis == ImGuiAxis_X) ? GetNavTweakPressedAmount(axis) + : -GetNavTweakPressedAmount(axis); if (input_delta != 0.0f) { + const bool tweak_slow = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) + ? ImGuiKey_NavGamepadTweakSlow : ImGuiKey_NavKeyboardTweakSlow); + const bool tweak_fast = IsKeyDown((g.NavInputSource == ImGuiInputSource_Gamepad) + ? ImGuiKey_NavGamepadTweakFast : ImGuiKey_NavKeyboardTweakFast); const int decimal_precision = is_floating_point ? ImParseFormatPrecision(format, 3) : 0; if (decimal_precision > 0) { // Gamepad/keyboard tweak speeds in % of slider bounds input_delta /= 100.0f; - if (IsNavInputDown(ImGuiNavInput_TweakSlow)) + if (tweak_slow) input_delta /= 10.0f; } - else if ((v_range >= -100.0f && v_range <= 100.0f) || IsNavInputDown(ImGuiNavInput_TweakSlow)) + else if ((v_range >= -100.0f && v_range <= 100.0f) || tweak_slow) // Gamepad/keyboard tweak speeds in integer steps input_delta = ((input_delta < 0.0f) ? -1.0f : +1.0f) / (float_t)v_range; else input_delta /= 100.0f; - if (IsNavInputDown(ImGuiNavInput_TweakFast)) + if (tweak_fast) input_delta *= 10.0f; g.SliderCurrentAccum += input_delta; @@ -2111,6 +2108,21 @@ namespace ImGui { } } + if (slider_sz < 1.0f) { + *out_grab_bb = ImRect(bb.Min, bb.Min); + } + else { + // Output grab position so it can be displayed by the caller + float_t grab_t = ScaleRatioFromValueT(data_type, *v, v_min, v_max, is_logarithmic, logarithmic_zero_epsilon, zero_deadzone_halfsize); + if (axis == ImGuiAxis_Y) + grab_t = 1.0f - grab_t; + const float_t grab_pos = ImLerp(slider_usable_pos_min, slider_usable_pos_max, grab_t); + if (axis == ImGuiAxis_X) + *out_grab_bb = ImRect(grab_pos - grab_sz * 0.5f, bb.Min.y + grab_padding, grab_pos + grab_sz * 0.5f, bb.Max.y - grab_padding); + else + *out_grab_bb = ImRect(bb.Min.x + grab_padding, grab_pos - grab_sz * 0.5f, bb.Max.x - grab_padding, grab_pos + grab_sz * 0.5f); + } + return value_changed; } diff --git a/src/ReDIVA/imgui_helper.hpp b/src/ReDIVA/imgui_helper.hpp index 7d383fa3..11e7921a 100644 --- a/src/ReDIVA/imgui_helper.hpp +++ b/src/ReDIVA/imgui_helper.hpp @@ -28,15 +28,15 @@ namespace ImGui { extern float_t ColumnSpace; - inline bool ItemKeyDown(int32_t key) { + inline bool ItemKeyDown(ImGuiKey key) { return IsItemFocused() && IsKeyDown(key); } - inline bool ItemKeyPressed(int32_t key, bool repeat) { + inline bool ItemKeyPressed(ImGuiKey key, bool repeat = true) { return IsItemFocused() && IsKeyPressed(key, repeat); } - inline bool ItemKeyReleased(int32_t key) { + inline bool ItemKeyReleased(ImGuiKey key) { return IsItemFocused() && IsKeyReleased(key); } diff --git a/src/ReDIVA/input.cpp b/src/ReDIVA/input.cpp index b0e6f31a..b2b28fe2 100644 --- a/src/ReDIVA/input.cpp +++ b/src/ReDIVA/input.cpp @@ -7,8 +7,8 @@ #include "input.hpp" #include +#include "../KKdLib/timer.hpp" #include "../KKdLib/vec.hpp" -#include "../CRE/timer.hpp" #define CIMGUI_DEFINE_ENUMS_AND_STRUCTS #include @@ -179,8 +179,8 @@ namespace Input { } void EndFrame() { - keys_prev = keys; - mouse_buttons_prev = mouse_buttons; + keys_prev.assign(keys.begin(), keys.end()); + mouse_buttons_prev.assign(mouse_buttons.begin(), mouse_buttons.end()); } void NewFrame() { diff --git a/src/ReDIVA/main.cpp b/src/ReDIVA/main.cpp index b4801c25..5ec16a7a 100644 --- a/src/ReDIVA/main.cpp +++ b/src/ReDIVA/main.cpp @@ -12,6 +12,7 @@ #include "../KKdLib/deflate.hpp" #include "../KKdLib/dsc.hpp" #include "../KKdLib/interpolation.hpp" +#include "../KKdLib/str_utils.hpp" #include "../CRE/auth_3d.hpp" #include "../CRE/shader_ft.hpp" #include "main.hpp" @@ -311,38 +312,46 @@ static void a3da_to_dft_dsc(int32_t pv_id) { } #if defined(ReDIVA_DEV) -void compile_shaders(farc* f, farc* of, const shader_table* shaders_table, const size_t size, - const shader_bind_func* bind_func_table, const size_t bind_func_table_size) { +struct program_spv { + size_t size; + size_t spv; +}; + +void compile_shaders(farc* f, farc* of, const shader_table* shaders_table, const size_t size, bool debug) { if (!f || !shaders_table || !size) return; wchar_t temp_path[MAX_PATH]; - GetTempPathW(MAX_PATH, temp_path); + if (FAILED(GetTempPathW(MAX_PATH, temp_path))) + return; - wchar_t glsl_vert_file_buf[MAX_PATH]; - swprintf_s(glsl_vert_file_buf, sizeof(glsl_vert_file_buf) - / sizeof(wchar_t), L"%sReDIVA.vert", temp_path); + wchar_t vulkan_sdk_path[MAX_PATH]; + if (FAILED(GetEnvironmentVariableW(L"VULKAN_SDK", vulkan_sdk_path, MAX_PATH))) + return; - wchar_t glsl_frag_file_buf[MAX_PATH]; - swprintf_s(glsl_frag_file_buf, sizeof(glsl_frag_file_buf) - / sizeof(wchar_t), L"%sReDIVA.frag", temp_path); + HANDLE std_output_handle = 0; + HANDLE std_error_handle = 0; +#if DEBUG + std_output_handle = GetStdHandle(STD_OUTPUT_HANDLE); + std_error_handle = GetStdHandle(STD_ERROR_HANDLE); +#endif - wchar_t spv_vert_file_buf[MAX_PATH]; - swprintf_s(spv_vert_file_buf, sizeof(spv_vert_file_buf) - / sizeof(wchar_t), L"%sReDIVA.vert.spv", temp_path); - - wchar_t spv_frag_file_buf[MAX_PATH]; - swprintf_s(spv_frag_file_buf, sizeof(spv_frag_file_buf) - / sizeof(wchar_t), L"%sReDIVA.frag.spv", temp_path); + const char* options; + if (debug) + options = "-O -g"; + else + options = "-O"; GLsizei buffer_size = 0x20000; - void* binary = force_malloc(buffer_size); + void* spv = force_malloc(buffer_size); size_t temp_vert_size = 0x10000; char* temp_vert = force_malloc_s(char, temp_vert_size); size_t temp_frag_size = 0x10000; char* temp_frag = force_malloc_s(char, temp_frag_size); std::vector vec_vert; std::vector vec_frag; + std::vector program_data_vert_spv; + std::vector program_data_frag_spv; for (size_t i = 0; i < size; i++) { const shader_table* shader_table = &shaders_table[i]; @@ -350,6 +359,8 @@ void compile_shaders(farc* f, farc* of, const shader_table* shaders_table, const const shader_sub_table* sub_table = shaders_table[i].sub; vec_vert.resize(shader_table->num_uniform); vec_frag.resize(shader_table->num_uniform); + int32_t* vec_vert_data = vec_vert.data(); + int32_t* vec_frag_data = vec_frag.data(); for (size_t j = 0; j < num_sub; j++, sub_table++) { char vert_file_buf[MAX_PATH]; strcpy_s(vert_file_buf, sizeof(vert_file_buf), sub_table->vp); @@ -385,63 +396,76 @@ void compile_shaders(farc* f, farc* of, const shader_table* shaders_table, const continue; } - vert_data = shader::parse_include(vert_data, f); - frag_data = shader::parse_include(frag_data, f); + vert_data = shader_opengl::parse_include(vert_data, f); + frag_data = shader_opengl::parse_include(frag_data, f); + + vert_data = shader_opengl::parse_include(vert_data, f); + frag_data = shader_opengl::parse_include(frag_data, f); wchar_t cmd_temp[0x800]; - if (shader_table->num_uniform > 0) { + char vert_buf[MAX_PATH]; + char frag_buf[MAX_PATH]; + char vert_bin_buf[MAX_PATH]; + char frag_bin_buf[MAX_PATH]; + wchar_t glsl_vert_file_buf[MAX_PATH]; + wchar_t glsl_frag_file_buf[MAX_PATH]; + wchar_t spv_vert_file_buf[MAX_PATH]; + wchar_t spv_frag_file_buf[MAX_PATH]; + + strcpy_s(vert_bin_buf, sizeof(vert_bin_buf), sub_table->vp); + strcat_s(vert_bin_buf, sizeof(vert_bin_buf), ".vert.bin"); + + strcpy_s(frag_bin_buf, sizeof(frag_bin_buf), sub_table->fp); + strcat_s(frag_bin_buf, sizeof(frag_bin_buf), ".frag.bin"); + + bool has_vert_bin = of->has_file(vert_bin_buf); + bool has_frag_bin = of->has_file(frag_bin_buf); + + if ((!has_vert_bin || !has_frag_bin) && shader_table->num_uniform > 0 + && (sub_table->vp_unival_max[0] != -1 || sub_table->fp_unival_max[0] != -1)) { int32_t num_uniform = shader_table->num_uniform; - size_t unival_curr = 1; - size_t unival_count = 1; + size_t unival_vp_curr = 1; + size_t unival_vp_count = 1; + size_t unival_fp_curr = 1; + size_t unival_fp_count = 1; const int32_t* vp_unival_max = sub_table->vp_unival_max; const int32_t* fp_unival_max = sub_table->fp_unival_max; for (size_t k = 0; k < num_uniform; k++) { - size_t unival_max = shader_table->use_permut[k] - ? max_def(vp_unival_max[k], fp_unival_max[k]) : 0; - unival_count += unival_curr * unival_max; - unival_curr *= unival_max + 1; + const size_t unival_vp_max = shader_table->use_permut[k] ? vp_unival_max[k] : 0; + const size_t unival_fp_max = shader_table->use_permut[k] ? fp_unival_max[k] : 0; + unival_vp_count += unival_vp_curr * unival_vp_max; + unival_fp_count += unival_fp_curr * unival_fp_max; + unival_vp_curr *= unival_vp_max + 1; + unival_fp_curr *= unival_fp_max + 1; } - char vert_buf[MAX_PATH]; - char frag_buf[MAX_PATH]; + if (!has_vert_bin) { + program_data_vert_spv.reserve(unival_vp_count); - strcpy_s(vert_buf, sizeof(vert_buf), sub_table->vp); - size_t vert_buf_pos = utf8_length(vert_buf); - vert_buf[vert_buf_pos++] = '.'; - vert_buf[vert_buf_pos] = 0; - memset(&vert_buf[vert_buf_pos], '0', num_uniform); - vert_buf[vert_buf_pos + num_uniform] = 0; - strcat_s(vert_buf, sizeof(vert_buf), ".vert.spv"); + strcpy_s(vert_buf, sizeof(vert_buf), sub_table->vp); + size_t vert_buf_pos = utf8_length(vert_buf); + vert_buf[vert_buf_pos++] = '.'; + vert_buf[vert_buf_pos] = 0; + memset(&vert_buf[vert_buf_pos], '0', num_uniform); + vert_buf[vert_buf_pos + num_uniform] = 0; + strcat_s(vert_buf, sizeof(vert_buf), ".vert.spv"); + int32_t vert_buf_len = (int32_t)utf8_length(vert_buf); - strcpy_s(frag_buf, sizeof(frag_buf), sub_table->fp); - size_t frag_buf_pos = utf8_length(frag_buf); - frag_buf[frag_buf_pos++] = '.'; - frag_buf[frag_buf_pos] = 0; - memset(&frag_buf[frag_buf_pos], '0', num_uniform); - frag_buf[frag_buf_pos + num_uniform] = 0; - strcat_s(frag_buf, sizeof(frag_buf), ".frag.spv"); + for (size_t k = 0; k < unival_vp_count; k++) { + for (size_t l = 0, m = k; l < num_uniform; l++) { + size_t unival_max = (size_t)(shader_table->use_permut[l] ? vp_unival_max[l] : 0) + 1; + vec_vert_data[l] = (uint32_t)(min_def(m % unival_max, vp_unival_max[l])); + m /= unival_max; + vert_buf[vert_buf_pos + l] = (char)('0' + vec_vert_data[l]); + } - for (size_t k = 0; k < unival_count; k++) { - for (size_t l = 0, m = k; l < num_uniform; l++) { - size_t unival_max = (size_t)(shader_table->use_permut[l] - ? max_def(vp_unival_max[l], fp_unival_max[l]) : 0) + 1; - vec_vert[l] = (uint32_t)(min_def(m % unival_max, vp_unival_max[l])); - m /= unival_max; - vert_buf[vert_buf_pos + l] = (char)('0' + vec_vert[l]); - } + shader_opengl::parse_define(vert_data, num_uniform, + vec_vert_data, &temp_vert, &temp_vert_size); - for (size_t l = 0, m = k; l < num_uniform; l++) { - size_t unival_max = (size_t)(shader_table->use_permut[l] - ? max_def(vp_unival_max[l], fp_unival_max[l]) : 0) + 1; - vec_frag[l] = (uint32_t)(min_def(m % unival_max, fp_unival_max[l])); - m /= unival_max; - frag_buf[frag_buf_pos + l] = (char)('0' + vec_frag[l]); - } - - if (!of->has_file(vert_buf)) { - shader::parse_define(vert_data, - num_uniform, vp_unival_max, fp_unival_max, - vec_vert.data(), &temp_vert, &temp_vert_size); + swprintf_s(glsl_vert_file_buf, sizeof(glsl_vert_file_buf) + / sizeof(wchar_t), L"%s%.*S", temp_path, vert_buf_len - 4, vert_buf); + swprintf_s(spv_vert_file_buf, sizeof(spv_vert_file_buf) + / sizeof(wchar_t), L"%s%S", temp_path, vert_buf); file_stream fs; fs.open(glsl_vert_file_buf, L"wb"); @@ -449,33 +473,68 @@ void compile_shaders(farc* f, farc* of, const shader_table* shaders_table, const fs.close(); swprintf_s(cmd_temp, sizeof(cmd_temp) / sizeof(wchar_t), - L"E:\\C\\VulkanSDK\\1.3.231.1\\Bin\\glslc.exe -DSPIRV" - " -O --target-spv=spv1.0 -o \"%s\" \"%s\"", - spv_vert_file_buf, glsl_vert_file_buf); + L"%s\\Bin\\glslc.exe -DSPIRV %S --target-spv=spv1.0 -o \"%s\" \"%s\"", + vulkan_sdk_path, options, spv_vert_file_buf, glsl_vert_file_buf); STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); + si.dwFlags |= STARTF_USESTDHANDLES; + si.hStdError = std_output_handle; + si.hStdOutput = std_error_handle; ZeroMemory(&pi, sizeof(pi)); CreateProcessW(0, cmd_temp, 0, 0, FALSE, CREATE_NO_WINDOW, 0, 0, &si, &pi); WaitForSingleObject(pi.hProcess, INFINITE); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); - farc_file* ff = of->add_file(vert_buf); fs.open(spv_vert_file_buf, L"rb"); - ff->size = fs.get_length(); - ff->data = force_malloc(ff->size); - fs.read(ff->data, ff->size); - fs.close(); - } + if (fs.check_not_null()) { + size_t size = fs.get_length(); + void* data = force_malloc(size); + fs.read(data, size); + fs.close(); - if (!of->has_file(frag_buf)) { - shader::parse_define(frag_data, - num_uniform, vp_unival_max, fp_unival_max, - vec_frag.data(), &temp_frag, &temp_frag_size); + program_data_vert_spv.push_back({ size, (size_t)data }); + } + else { + printf("There was an error while compiling %.*s\n", vert_buf_len - 4, vert_buf ); + program_data_vert_spv.push_back({ 0, 0 }); + } + + DeleteFileW(glsl_vert_file_buf); + DeleteFileW(spv_vert_file_buf); + } + } + + if (!has_frag_bin) { + program_data_frag_spv.reserve(unival_fp_count); + + strcpy_s(frag_buf, sizeof(frag_buf), sub_table->fp); + size_t frag_buf_pos = utf8_length(frag_buf); + frag_buf[frag_buf_pos++] = '.'; + frag_buf[frag_buf_pos] = 0; + memset(&frag_buf[frag_buf_pos], '0', num_uniform); + frag_buf[frag_buf_pos + num_uniform] = 0; + strcat_s(frag_buf, sizeof(frag_buf), ".frag.spv"); + int32_t frag_buf_len = (int32_t)utf8_length(frag_buf); + + for (size_t k = 0; k < unival_fp_count; k++) { + for (size_t l = 0, m = k; l < num_uniform; l++) { + size_t unival_max = (size_t)(shader_table->use_permut[l] ? fp_unival_max[l] : 0) + 1; + vec_frag_data[l] = (uint32_t)(min_def(m % unival_max, fp_unival_max[l])); + m /= unival_max; + frag_buf[frag_buf_pos + l] = (char)('0' + vec_frag_data[l]); + } + shader_opengl::parse_define(frag_data, num_uniform, + vec_frag_data, &temp_frag, &temp_frag_size); + + swprintf_s(glsl_frag_file_buf, sizeof(glsl_frag_file_buf) + / sizeof(wchar_t), L"%s%.*S", temp_path, frag_buf_len - 4, frag_buf); + swprintf_s(spv_frag_file_buf, sizeof(spv_frag_file_buf) + / sizeof(wchar_t), L"%s%S", temp_path, frag_buf); file_stream fs; fs.open(glsl_frag_file_buf, L"wb"); @@ -483,41 +542,56 @@ void compile_shaders(farc* f, farc* of, const shader_table* shaders_table, const fs.close(); swprintf_s(cmd_temp, sizeof(cmd_temp) / sizeof(wchar_t), - L"E:\\C\\VulkanSDK\\1.3.231.1\\Bin\\glslc.exe -DSPIRV" - " -O --target-spv=spv1.0 -o \"%s\" \"%s\"", - spv_frag_file_buf, glsl_frag_file_buf); + L"%s\\Bin\\glslc.exe -DSPIRV %S --target-spv=spv1.0 -o \"%s\" \"%s\"", + vulkan_sdk_path, options, spv_frag_file_buf, glsl_frag_file_buf); STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); + si.dwFlags |= STARTF_USESTDHANDLES; + si.hStdError = std_output_handle; + si.hStdOutput = std_error_handle; ZeroMemory(&pi, sizeof(pi)); CreateProcessW(0, cmd_temp, 0, 0, FALSE, CREATE_NO_WINDOW, 0, 0, &si, &pi); WaitForSingleObject(pi.hProcess, INFINITE); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); - farc_file* ff = of->add_file(frag_buf); fs.open(spv_frag_file_buf, L"rb"); - ff->size = fs.get_length(); - ff->data = force_malloc(ff->size); - fs.read(ff->data, ff->size); - fs.close(); + if (fs.check_not_null()) { + size_t size = fs.get_length(); + void* data = force_malloc(size); + fs.read(data, size); + fs.close(); + + program_data_frag_spv.push_back({ size, (size_t)data }); + } + else { + printf("There was an error while compiling %.*s\n", frag_buf_len - 4, frag_buf); + program_data_frag_spv.push_back({ 0, 0 }); + } + + DeleteFileW(glsl_frag_file_buf); + DeleteFileW(spv_frag_file_buf); } } } - else { - char vert_buf[MAX_PATH]; - char frag_buf[MAX_PATH]; - strcpy_s(vert_buf, sizeof(vert_buf), sub_table->vp); - strcpy_s(frag_buf, sizeof(vert_buf), sub_table->fp); - strcat_s(vert_buf, sizeof(vert_buf), "..vert"); - strcat_s(frag_buf, sizeof(vert_buf), "..frag"); + else if (!has_vert_bin || !has_frag_bin) { + if (!has_vert_bin) { + program_data_vert_spv.reserve(1); - if (!of->has_file(vert_buf)) { - shader::parse_define(vert_data, - 0, 0, 0, 0, &temp_vert, &temp_vert_size); + strcpy_s(vert_buf, sizeof(vert_buf), sub_table->vp); + strcat_s(vert_buf, sizeof(vert_buf), "..vert.spv"); + int32_t vert_buf_len = (int32_t)utf8_length(vert_buf); + + shader_opengl::parse_define(vert_data, &temp_vert, &temp_vert_size); + + swprintf_s(glsl_vert_file_buf, sizeof(glsl_vert_file_buf) + / sizeof(wchar_t), L"%s%.*S", temp_path, vert_buf_len - 4, vert_buf); + swprintf_s(spv_vert_file_buf, sizeof(spv_vert_file_buf) + / sizeof(wchar_t), L"%s%S", temp_path, vert_buf); file_stream fs; fs.open(glsl_vert_file_buf, L"wb"); @@ -525,32 +599,54 @@ void compile_shaders(farc* f, farc* of, const shader_table* shaders_table, const fs.close(); swprintf_s(cmd_temp, sizeof(cmd_temp) / sizeof(wchar_t), - L"E:\\C\\VulkanSDK\\1.3.231.1\\Bin\\glslc.exe -DSPIRV" - " -O --target-spv=spv1.0 -o \"%s\" \"%s\"", - spv_vert_file_buf, glsl_vert_file_buf); + L"%s\\Bin\\glslc.exe -DSPIRV %S --target-spv=spv1.0 -o \"%s\" \"%s\"", + vulkan_sdk_path, options, spv_vert_file_buf, glsl_vert_file_buf); STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); + si.dwFlags |= STARTF_USESTDHANDLES; + si.hStdError = std_output_handle; + si.hStdOutput = std_error_handle; ZeroMemory(&pi, sizeof(pi)); CreateProcessW(0, cmd_temp, 0, 0, FALSE, CREATE_NO_WINDOW, 0, 0, &si, &pi); WaitForSingleObject(pi.hProcess, INFINITE); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); - farc_file* ff = of->add_file(vert_buf); fs.open(spv_vert_file_buf, L"rb"); - ff->size = fs.get_length(); - ff->data = force_malloc(ff->size); - fs.read(ff->data, ff->size); - fs.close(); + if (fs.check_not_null()) { + size_t size = fs.get_length(); + void* data = force_malloc(size); + fs.read(data, size); + fs.close(); + + program_data_vert_spv.push_back({ size, (size_t)data }); + } + else { + printf("There was an error while compiling %.*s\n", vert_buf_len - 4, vert_buf); + program_data_vert_spv.push_back({ 0, 0 }); + } + + DeleteFileW(glsl_vert_file_buf); + DeleteFileW(spv_vert_file_buf); } - if (!of->has_file(frag_buf)) { - shader::parse_define(frag_data, - 0, 0, 0, 0, &temp_frag, &temp_frag_size); + if (!has_frag_bin) { + program_data_frag_spv.reserve(1); + + strcpy_s(frag_buf, sizeof(frag_buf), sub_table->fp); + strcat_s(frag_buf, sizeof(frag_buf), "..frag.spv"); + int32_t frag_buf_len = (int32_t)utf8_length(frag_buf); + + shader_opengl::parse_define(frag_data, &temp_frag, &temp_frag_size); + + swprintf_s(glsl_frag_file_buf, sizeof(glsl_frag_file_buf) + / sizeof(wchar_t), L"%s%.*S", temp_path, frag_buf_len - 4, frag_buf); + swprintf_s(spv_frag_file_buf, sizeof(spv_frag_file_buf) + / sizeof(wchar_t), L"%s%S", temp_path, frag_buf); file_stream fs; fs.open(glsl_frag_file_buf, L"wb"); @@ -558,67 +654,127 @@ void compile_shaders(farc* f, farc* of, const shader_table* shaders_table, const fs.close(); swprintf_s(cmd_temp, sizeof(cmd_temp) / sizeof(wchar_t), - L"E:\\C\\VulkanSDK\\1.3.231.1\\Bin\\glslc.exe -DSPIRV" - " -O --target-spv=spv1.0 -o \"%s\" \"%s\"", - spv_frag_file_buf, glsl_frag_file_buf); + L"%s\\Bin\\glslc.exe -DSPIRV %S --target-spv=spv1.0 -o \"%s\" \"%s\"", + vulkan_sdk_path, options, spv_frag_file_buf, glsl_frag_file_buf); STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); + si.dwFlags |= STARTF_USESTDHANDLES; + si.hStdError = std_output_handle; + si.hStdOutput = std_error_handle; ZeroMemory(&pi, sizeof(pi)); CreateProcessW(0, cmd_temp, 0, 0, FALSE, CREATE_NO_WINDOW, 0, 0, &si, &pi); WaitForSingleObject(pi.hProcess, INFINITE); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); - farc_file* ff = of->add_file(frag_buf); fs.open(spv_frag_file_buf, L"rb"); - ff->size = fs.get_length(); - ff->data = force_malloc(ff->size); - fs.read(ff->data, ff->size); - fs.close(); + if (fs.check_not_null()) { + size_t size = fs.get_length(); + void* data = force_malloc(size); + fs.read(data, size); + fs.close(); + + program_data_frag_spv.push_back({ size,(size_t)data }); + } + else { + printf("There was an error while compiling %.*s\n", frag_buf_len - 4, vert_buf); + program_data_frag_spv.push_back({ 0, 0 }); + } + + DeleteFileW(glsl_frag_file_buf); + DeleteFileW(spv_frag_file_buf); + } + } + + if (!has_vert_bin) { + farc_file* shader_vert_file = of->add_file(vert_bin_buf); + size_t vert_spv_count = program_data_vert_spv.size(); + size_t vert_spv_size = vert_spv_count * sizeof(program_spv); + for (program_spv& k : program_data_vert_spv) + vert_spv_size += align_val(k.size, 0x10); + shader_vert_file->data = force_malloc(vert_spv_size); + shader_vert_file->size = vert_spv_size; + shader_vert_file->data_changed = true; + + program_spv* vert_spv = (program_spv*)shader_vert_file->data; + size_t vert_spv_data_base = (size_t)shader_vert_file->data; + size_t vert_spv_data_offset = vert_spv_count * sizeof(program_spv); + for (program_spv& k : program_data_vert_spv) { + vert_spv->size = k.size; + vert_spv->spv = vert_spv_data_offset; + memcpy((void*)(vert_spv_data_base + vert_spv_data_offset), (void*)k.spv, k.size); + vert_spv_data_offset += align_val(k.size, 0x10); + void* spv_data = (void*)k.spv; + free_def(spv_data); + k.spv = 0; + vert_spv++; + } + } + + if (!has_frag_bin) { + farc_file* shader_frag_file = of->add_file(frag_bin_buf); + size_t frag_spv_count = program_data_frag_spv.size(); + size_t frag_spv_size = frag_spv_count * sizeof(program_spv); + for (program_spv& k : program_data_frag_spv) + frag_spv_size += align_val(k.size, 0x10); + shader_frag_file->data = force_malloc(frag_spv_size); + shader_frag_file->size = frag_spv_size; + shader_frag_file->data_changed = true; + + program_spv* frag_spv = (program_spv*)shader_frag_file->data; + size_t frag_spv_data_base = (size_t)shader_frag_file->data; + size_t frag_spv_data_offset = frag_spv_count * sizeof(program_spv); + for (program_spv& k : program_data_frag_spv) { + frag_spv->size = k.size; + frag_spv->spv = frag_spv_data_offset; + memcpy((void*)(frag_spv_data_base + frag_spv_data_offset), (void*)k.spv, k.size); + frag_spv_data_offset += align_val(k.size, 0x10); + void* spv_data = (void*)k.spv; + free_def(spv_data); + k.spv = 0; + frag_spv++; } } free_def(vert_data); free_def(frag_data); + program_data_vert_spv.clear(); + program_data_frag_spv.clear(); } vec_vert.clear(); vec_frag.clear(); } - free_def(binary); + free_def(spv); free_def(temp_vert); free_def(temp_frag); - - DeleteFileW(glsl_vert_file_buf); - DeleteFileW(glsl_frag_file_buf); - DeleteFileW(spv_vert_file_buf); - DeleteFileW(spv_frag_file_buf); } -void compile_all_shaders() { +void compile_all_shaders(bool debug) { farc f; f.read("rom\\ft_shaders.farc", true, false); farc of; - compile_shaders(&f, &of, shader_ft_table, shader_ft_table_size, - shader_ft_bind_func_table, shader_ft_bind_func_table_size); + compile_shaders(&f, &of, shader_ft_table, shader_ft_table_size, debug); of.write("rom\\ft_shaders_spirv", FARC_COMPRESS_FArC, false); } #endif int32_t wmain(int32_t argc, wchar_t** argv) { +#if defined(ReDIVA_DEV) + if (argc >= 2 && !wcscmp(argv[1], L"--compile-spir-v")) { + compile_all_shaders(argc >= 3 && !wcscmp(argv[2], L"-d")); + return 0; + } +#endif + //ShowWindow(GetConsoleWindow(), SW_HIDE); timeBeginPeriod(1); SetProcessDPIAware(); -#if defined(ReDIVA_DEV) - if (argc >= 2 && !wcscmp(argv[1], L"--compile-spir-v")) - compile_all_shaders(); -#endif - /* uint8_t key[] = { 0xA4, 0xB7, 0x31, 0xD0, 0x33, 0xFB, 0x4A, 0x63, 0x9D, 0xD2, 0x46, 0xA5, 0x05, 0xCD, 0x4B, 0xE5, @@ -700,6 +856,8 @@ int32_t wmain(int32_t argc, wchar_t** argv) { //a3da_to_dft_dsc(269); render_init_struct ris; + //ris.vulkan_render = true; + ris.vulkan_render = false; render_main(&ris); diff --git a/src/ReDIVA/pv_game.cpp b/src/ReDIVA/pv_game.cpp index 8903e539..af21c746 100644 --- a/src/ReDIVA/pv_game.cpp +++ b/src/ReDIVA/pv_game.cpp @@ -1,11 +1,8 @@ /* by korenkonder GitHub/GitLab: korenkonder - - Some code is from LearnOpenGL */ -#if defined(ReDIVA_DEV) #include #include "pv_game.hpp" #include "../CRE/Glitter/glitter.hpp" @@ -131,23 +128,11 @@ enum dsc_ft_func { DSC_FT_MAX, }; -pv_game_old pv_game_old_data; +pv_game _pv_game; TaskPvGame task_pv_game; extern render_context* rctx_ptr; -static float_t dsc_time_to_frame(int64_t time); -static void pv_game_change_field(pv_game_old* pvgm, int32_t field, int64_t dsc_time, int64_t curr_time); -static bool pv_game_dsc_process(pv_game_old* pvgm, int64_t curr_time); -static void pv_game_reset_field(pv_game_old* pvgm); - -static dsc_data* pv_game_dsc_data_find_func(pv_game_old* pvgm, dsc_ft_func func_name, - int32_t* time, int32_t* pv_branch_mode, dsc_data* start, dsc_data* end); -static void pv_game_dsc_data_find_playdata_item_anim(pv_game_old* pvgm, int32_t chara_id); -static void pv_game_dsc_data_find_playdata_set_motion(pv_game_old* pvgm, int32_t chara_id); -static void pv_game_dsc_data_set_motion_max_frame(pv_game_old* pvgm, - int32_t chara_id, int32_t motion_index, int64_t disp_time); - pv_play_data_motion_data::pv_play_data_motion_data() : rob_chr(), current_time(), duration(), start_pos(), end_pos(), start_rot(), end_rot() { mot_smooth_len = 12.0f; @@ -401,523 +386,7 @@ void TaskPvGame::Disp() { } -pv_game_old::pv_game_old() : state(), frame(), frame_float(), time(), rob_chara_ids(), -dsc_time(), dsc_data_ptr(), dsc_data_ptr_end(), play(), success(), chara_id(), -pv_end(), playdata(), scene_rot_y(), branch_mode(), pause(), step_frame() { - pv_id = -1; - light_auth_3d_id = {}; - camera_auth_3d_id = {}; - target_anim_fps = 60.0f; - anim_frame_speed = 1.0f; - scene_rot_mat = mat4_identity; -} - -pv_game_old::~pv_game_old() { - -} - -bool pv_game_old::Init() { - task_stage_load_task("PV_STAGE"); - return true; -} - -bool pv_game_old::Ctrl() { - switch (state) { - case 0: - return false; - case 1: { - bool wait_load = false; - for (int32_t i = 0; i < ROB_CHARA_COUNT; i++) - if (rob_chara_ids[i] != -1) - wait_load |= !task_rob_manager_check_chara_loaded(rob_chara_ids[i]); - - if (!wait_load) - state = 2; - } break; - case 2: { - data_struct* aft_data = &data_list[DATA_AFT]; - - light_param_data_storage_data_set_pv_id(pv_id); - - for (std::string& i : category_load) - auth_3d_data_load_category(i.c_str()); - - state = 3; - } break; - case 3: { - bool wait_load = task_stage_check_not_loaded(); - - for (uint32_t& i : objset_load) - if (object_storage_get_obj_set_handler(i) - && object_storage_load_obj_set_check_not_read(i)) - wait_load = true; - - for (std::string& i : category_load) - if (!auth_3d_data_check_category_loaded(i.c_str())) - wait_load = true; - - if (!wait_load) - state = 6; - } break; - case 6: { - data_struct* aft_data = &data_list[DATA_AFT]; - auth_3d_database* auth_3d_db = &aft_data->data_ft.auth_3d_db; - - for (uint32_t& i : objset_load) { - obj_set* set = object_storage_get_obj_set(i); - if (!set) - continue; - - for (uint32_t i = 0; i < set->obj_num; i++) { - obj* obj = &set->obj_data[i]; - for (uint32_t j = 0; j < obj->num_material; j++) { - obj_material* material = &obj->material_array[j].material; - if (material->color.specular == 0.5f) - *(vec3*)&material->color.specular = 0.0f; - else if (material->color.specular != 0.0f) - *(vec3*)&material->color.specular = { 0.1f, 0.1f, 0.1f }; - } - } - } - - light_auth_3d_id = {}; - { - char buf[0x200]; - sprintf_s(buf, sizeof(buf), "STGPV%03d_EFF_LT_000", pv_id); - uint32_t light_auth_3d_uid = auth_3d_db->get_uid(buf); - if (light_auth_3d_uid != -1) { - light_auth_3d_id = auth_3d_data_load_uid(light_auth_3d_uid, auth_3d_db); - light_auth_3d_id.read_file(auth_3d_db); - light_auth_3d_id.set_enable(false); - light_auth_3d_id.set_visibility(false); - break; - } - } - - camera_auth_3d_id = {}; - { - char buf[0x200]; - sprintf_s(buf, sizeof(buf), "CAMPV%03d_BASE", pv_id); - uint32_t camera_auth_3d_uid = auth_3d_db->get_uid(buf); - camera_auth_3d_id = auth_3d_data_load_uid(camera_auth_3d_uid, auth_3d_db); - camera_auth_3d_id.read_file(auth_3d_db); - camera_auth_3d_id.set_enable(false); - camera_auth_3d_id.set_visibility(false); - } - - state = 7; - } break; - case 7: { - bool wait_load = false; - - for (auto i : pv_auth_3d_ids) - if (!i.second.check_loaded()) - wait_load = true; - - if (!light_auth_3d_id.check_loaded()) - wait_load = true; - - if (!camera_auth_3d_id.check_loaded()) - wait_load = true; - - if (!wait_load) - state = 8; - } break; - case 8: { - app::TaskWork::AppendTask(&pv_param_task::post_process_task, "PV POST PROCESS TASK"); - - state = 9; - } break; - case 9: { - { - data_struct* aft_data = &data_list[DATA_AFT]; - - dsc dsc_mouth; - dsc dsc_scene; - dsc dsc_system; - dsc dsc_easy; - - char path_buf[0x200]; - char file_buf[0x200]; - - farc dsc_common_farc; - sprintf_s(path_buf, sizeof(path_buf), "root+/pv_script/pv%03d/", pv_id); - sprintf_s(file_buf, sizeof(file_buf), "pv_%03d_common.farc", pv_id); - aft_data->load_file(&dsc_common_farc, path_buf, file_buf, farc::load_file); - - sprintf_s(file_buf, sizeof(file_buf), "pv_%03d_mouth.dsc", pv_id); - farc_file* dsc_mouth_ff = dsc_common_farc.read_file(file_buf); - if (dsc_mouth_ff) - dsc_mouth.parse(dsc_mouth_ff->data, dsc_mouth_ff->size, DSC_X); - - sprintf_s(file_buf, sizeof(file_buf), "pv_%03d_scene.dsc", pv_id); - farc_file* dsc_scene_ff = dsc_common_farc.read_file(file_buf); - if (dsc_scene_ff) - dsc_scene.parse(dsc_scene_ff->data, dsc_scene_ff->size, DSC_X); - - sprintf_s(file_buf, sizeof(file_buf), "pv_%03d_system.dsc", pv_id); - farc_file* dsc_system_ff = dsc_common_farc.read_file(file_buf); - if (dsc_system_ff) - dsc_system.parse(dsc_system_ff->data, dsc_system_ff->size, DSC_X); - - sprintf_s(file_buf, sizeof(file_buf), "pv_%03d_easy.dsc", pv_id); - dsc_easy.type = DSC_X; - aft_data->load_file(&dsc_easy, path_buf, file_buf, dsc::load_file); - - dsc_m.merge(4, &dsc_mouth, &dsc_scene, &dsc_system, &dsc_easy); - - struct miku_state { - bool disp; - int32_t disp_time; - int32_t set_motion_time; - uint32_t set_motion_data_offset; - } state[6]; - - for (miku_state& i : state) { - i.disp = true; - i.disp_time = -1; - i.set_motion_time = -1; - i.set_motion_data_offset = 0; - } - } - - for (int32_t i = 0; i < ROB_CHARA_COUNT; i++) { - playdata[i].reset(); - if (rob_chara_ids[i] == -1) - continue; - - rob_chara* rob_chr = rob_chara_array_get(rob_chara_ids[i]); - if (rob_chr) { - playdata[i].rob_chr = rob_chr; - rob_chr->frame_speed = anim_frame_speed; - rob_chr->data.motion.step_data.step = anim_frame_speed; - //sub_1404F3000(i, rob_chr, anim_frame_speed); - } - pv_game_dsc_data_find_playdata_set_motion(this, i); - pv_game_dsc_data_find_playdata_item_anim(this, i); - } - - dsc_data_ptr = dsc_m.data.data(); - dsc_data_ptr_end = dsc_data_ptr + dsc_m.data.size(); - - state = 10; - - pv_game_change_field(this, 1, -1, -1); - - Glitter::glt_particle_manager->SetPause(false); - extern float_t frame_speed; - frame_speed = 1.0f; - - pause = false; - step_frame = false; - - frame_float = -1.0f; - this->frame = -1; - this->time = (int64_t)round(-(100000.0 / 60.0) * 10000.0); - while (dsc_data_ptr != dsc_data_ptr_end - && pv_game_dsc_process(this, this->time)) - dsc_data_ptr++; - - light_auth_3d_id.set_enable(true); - light_auth_3d_id.set_camera_root_update(false); - light_auth_3d_id.set_paused(false); - light_auth_3d_id.set_repeat(false); - light_auth_3d_id.set_visibility(true); - - camera_auth_3d_id.set_enable(true); - camera_auth_3d_id.set_camera_root_update(true); - camera_auth_3d_id.set_paused(false); - camera_auth_3d_id.set_repeat(false); - camera_auth_3d_id.set_visibility(true); - - pause = true; - } break; - case 10: { - float_t delta_frame = get_delta_frame(); - frame_float += delta_frame; - frame = (int32_t)frame_float; - time = (int64_t)round(frame_float * (100000.0 / 60.0) * 10000.0); - - while (dsc_data_ptr != dsc_data_ptr_end - && pv_game_dsc_process(this, time)) - dsc_data_ptr++; - - if (!play || pv_end) - state = 11; - } break; - case 11: { - Glitter::glt_particle_manager->FreeScenes(); - SetDest(); - } break; - } - return false; -} - -bool pv_game_old::Dest() { - Unload(); - task_stage_unload_task(); - - light_param_data_storage_data_reset(); - rctx_ptr->post_process.tone_map->set_saturate_coeff(1.0f); - rctx_ptr->post_process.tone_map->set_scene_fade(0.0f); - rctx_ptr->post_process.tone_map->set_scene_fade_blend_func(0); - rctx_ptr->post_process.dof->data.pv.enable = false; - rctx_ptr->object_data.object_culling = true; - rctx_ptr->draw_pass.shadow_ptr->range = 1.0f; - - Glitter::glt_particle_manager->SetPause(false); - extern float_t frame_speed; - frame_speed = 1.0f; - return true; -} - -void pv_game_old::Disp() { - if (state != 10) - return; - -} - -void pv_game_old::Basic() { - if (state != 10) - return; - - if (step_frame) - if (pause) - pause = false; - else { - pause = true; - step_frame = false; - } - - Glitter::glt_particle_manager->SetPause(pause); - extern float_t frame_speed; - frame_speed = pause ? 0.0f : 1.0f; -} - -void pv_game_old::Window() { - if (state != 10) - return; - - if (Input::IsKeyTapped(GLFW_KEY_K, GLFW_MOD_CONTROL)) - state = 11; - else if (Input::IsKeyTapped(GLFW_KEY_K)) - pause ^= true; - - if (Input::IsKeyTapped(GLFW_KEY_L)) { - pause = true; - step_frame = true; - } - - if (!pause) - return; - - ImGuiIO& io = ImGui::GetIO(); - ImGuiStyle& style = ImGui::GetStyle(); - ImFont* font = ImGui::GetFont(); - - float_t w = 240.0f; - float_t h = 86.0f; - - extern int32_t width; - ImGui::SetNextWindowPos({ width - w, 0 }, ImGuiCond_Always); - ImGui::SetNextWindowSize({ w, h }, ImGuiCond_Always); - - ImGuiWindowFlags window_flags = 0; - window_flags |= ImGuiWindowFlags_NoResize; - - bool collapsed = !ImGui::Begin("X PV GAME", 0, window_flags); - if (collapsed) { - ImGui::End(); - return; - } - - w = ImGui::GetContentRegionAvailWidth(); - if (ImGui::BeginTable("buttons", 2)) { - ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed, w * 0.5f); - - ImGui::TableNextColumn(); - w = ImGui::GetContentRegionAvailWidth(); - if (ImGui::ButtonEnterKeyPressed(pause || (!pause && step_frame) ? "Play (K)" : "Pause (K)", { w, 0.0f })) - pause ^= true; - - ImGui::TableNextColumn(); - w = ImGui::GetContentRegionAvailWidth(); - if (ImGui::ButtonEnterKeyPressed("Step Frame (L)", { w, 0.0f })) { - pause = true; - step_frame = true; - } - - ImGui::TableNextColumn(); - w = ImGui::GetContentRegionAvailWidth(); - if (ImGui::ButtonEnterKeyPressed("Stop (Ctrl+K)", { w, 0.0f })) - state = 11; - - ImGui::TableNextColumn(); - w = ImGui::GetContentRegionAvailWidth(); - char buf[0x100]; - sprintf_s(buf, sizeof(buf), "%d", frame); - ImGui::PushStyleColor(ImGuiCol_Button, { 0.0f, 0.0f, 0.0f, 0.0f }); - ImGui::PushStyleColor(ImGuiCol_ButtonActive, { 0.0f, 0.0f, 0.0f, 0.0f }); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, { 0.0f, 0.0f, 0.0f, 0.0f }); - ImGui::ButtonExEnterKeyPressed(buf, { w, 0.0f }, ImGuiButtonFlags_DontClosePopups - | ImGuiButtonFlags_NoNavFocus | ImGuiButtonFlags_NoHoveredOnFocus); - ImGui::PopStyleColor(3); - ImGui::EndTable(); - } - - extern bool input_locked; - input_locked |= ImGui::IsWindowFocused(); - ImGui::End(); -} - -void pv_game_old::Load(int32_t pv_id, chara_index charas[6], int32_t modules[6]) { - data_struct* aft_data = &data_list[DATA_AFT]; - motion_database* aft_mot_db = &aft_data->data_ft.mot_db; - object_database* aft_obj_db = &aft_data->data_ft.obj_db; - - this->pv_id = pv_id; - - char buf[0x200]; - sprintf_s(buf, sizeof(buf), "PV%03d", pv_id); - uint32_t mot_set = aft_mot_db->get_motion_set_id(buf); - motion_set_load_motion(mot_set, 0, aft_mot_db); - motion_set_load_mothead(mot_set, 0, aft_mot_db); - - sprintf_s(buf, sizeof(buf), "EFFSTGPV%03d", pv_id); - stage_category = buf; - category_load.push_back(stage_category); - - - sprintf_s(buf, sizeof(buf), "CAMPV%03d", pv_id); - camera_category = buf; - category_load.push_back(camera_category); - - play = true; - success = true; - chara_id = 0; - target_anim_fps = get_target_anim_fps(); - anim_frame_speed = get_anim_frame_speed(); - pv_end = false; - for (pv_play_data& i : playdata) - i.reset(); - scene_rot_y = 0.0f; - scene_rot_mat = mat4_identity; - branch_mode = 0; - - pause = false; - step_frame = false; - - for (int32_t& i : rob_chara_ids) - i = -1; - - for (uint32_t& i : objset_load) - object_storage_load_set(aft_data, aft_obj_db, i); - - state = 1; - this->frame = 0; - frame_float = 0.0; - time = 0; - - dsc_m.type = DSC_NONE; - dsc_m.signature = 0; - dsc_m.id = 0; - dsc_m.data.clear(); - dsc_m.data.shrink_to_fit(); - dsc_m.data_buffer.clear(); - dsc_m.data_buffer.shrink_to_fit(); - dsc_data_ptr = 0; - dsc_data_ptr_end = 0; - - dsc_time = 0; - dsc_data_ptr = 0; - dsc_data_ptr_end = 0; - - Glitter::glt_particle_manager->draw_all = false; -} - -void pv_game_old::Unload() { - data_struct* aft_data = &data_list[DATA_AFT]; - motion_database* aft_mot_db = &aft_data->data_ft.mot_db; - - char buf[0x200]; - sprintf_s(buf, sizeof(buf), "PV%03d", pv_id); - uint32_t mot_set = aft_mot_db->get_motion_set_id(buf); - motion_set_unload_motion(mot_set); - motion_set_unload_mothead(mot_set); - - pv_id = -1; - - state = 0; - frame = 0; - frame_float = 0.0; - time = 0; - - for (int32_t& i : rob_chara_ids) - if (i != -1) { - rob_chara_array_free_chara_id(i); - i = -1; - } - - for (auto& i : pv_auth_3d_ids) - i.second.unload_id(rctx_ptr); - - light_auth_3d_id.unload_id(rctx_ptr); - camera_auth_3d_id.unload_id(rctx_ptr); - - pv_auth_3d_ids.clear(); - light_auth_3d_id = {}; - camera_auth_3d_id = {}; - - for (std::string& i : category_load) - auth_3d_data_unload_category(i.c_str()); - - category_load.clear(); - category_load.shrink_to_fit(); - - pv_category.clear(); - pv_category.shrink_to_fit(); - stage_category.clear(); - stage_category.shrink_to_fit(); - camera_category.clear(); - camera_category.shrink_to_fit(); - - for (uint32_t& i : objset_load) - object_storage_unload_set(i); - - objset_load.clear(); - objset_load.shrink_to_fit(); - - dsc_m.type = DSC_NONE; - dsc_m.signature = 0; - dsc_m.id = 0; - dsc_m.data.clear(); - dsc_m.data.shrink_to_fit(); - dsc_m.data_buffer.clear(); - dsc_m.data_buffer.shrink_to_fit(); - dsc_data_ptr = 0; - dsc_data_ptr_end = 0; - - dsc_time = 0; - dsc_data_ptr = 0; - dsc_data_ptr_end = 0; - - play = true; - success = true; - chara_id = 0; - target_anim_fps = get_target_anim_fps(); - anim_frame_speed = get_anim_frame_speed(); - pv_end = false; - for (pv_play_data& i : playdata) - i.reset(); - scene_rot_y = 0.0f; - scene_rot_mat = mat4_identity; - branch_mode = 0; - - Glitter::glt_particle_manager->draw_all = true; - pv_param_task::post_process_task.SetDest(); -} - -bool task_pv_game_append_task(TaskPvGame::InitData* init_data) { +bool task_pv_game_add_task(TaskPvGame::InitData* init_data) { task_pv_game.data.Reset(); task_pv_game.data.field_0 = 1; @@ -944,1149 +413,19 @@ bool task_pv_game_append_task(TaskPvGame::InitData* init_data) { task_pv_game.data.field_198 = init_data->field_19C; if (init_data->field_198) - return app::TaskWork::AppendTask(&task_pv_game, "PVGAME"); + return app::TaskWork::AddTask(&task_pv_game, "PVGAME"); else - return app::TaskWork::AppendTask(&task_pv_game, 0, "PVGAME"); + return app::TaskWork::AddTask(&task_pv_game, 0, "PVGAME"); } bool task_pv_game_check_task_ready() { return app::TaskWork::CheckTaskReady(&task_pv_game); } -bool task_pv_game_free_task() { +bool task_pv_game_del_task() { if (!app::TaskWork::CheckTaskReady(&task_pv_game)) return true; - task_pv_game.SetDest(); + task_pv_game.DelTask(); return false; } - -static float_t dsc_time_to_frame(int64_t time) { - return (float_t)time / 1000000000.0f * 60.0f; -} - -static void pv_game_change_field(pv_game_old* pvgm, int32_t field, int64_t dsc_time, int64_t curr_time) { - light_param_data_storage_data_set_pv_cut(field); -} - -static bool pv_game_dsc_process(pv_game_old* a1, int64_t curr_time) { - dsc_ft_func func = (dsc_ft_func)a1->dsc_data_ptr->func; - uint32_t* data = a1->dsc_m.get_func_data(a1->dsc_data_ptr); - if (a1->branch_mode) { - bool v19; - if (a1->success) - v19 = a1->branch_mode == 2; - else - v19 = a1->branch_mode == 1; - if (!v19) { - if (func < 0 || func > DSC_FT_AGEAGE_CTRL) { - a1->play = false; - return false; - } - else if ((data[0] < 0 || data[0] > 1) - && func != DSC_FT_PV_END && func != DSC_FT_PV_BRANCH_MODE) - return true; - } - } - - - - switch (func) { - case DSC_FT_END: { - a1->play = false; - a1->state = 11; - return false; - } break; - case DSC_FT_TIME: { - a1->dsc_time = (int64_t)(int32_t)data[0] * 10000; - if (a1->dsc_time > curr_time) - return false; - } break; - case DSC_FT_MIKU_MOVE: { - a1->chara_id = (int32_t)data[0]; - pv_play_data* playdata = &a1->playdata[a1->chara_id]; - - vec3 trans; - trans.x = (float_t)(int32_t)data[1] * 0.001f; - trans.y = (float_t)(int32_t)data[2] * 0.001f; - trans.z = (float_t)(int32_t)data[3] * 0.001f; - - rob_chara* rob_chr = playdata->rob_chr; - if (!rob_chr) - break; - - mat4_mult_vec3_trans(&a1->scene_rot_mat, - &trans, &rob_chr->data.miku_rot.position); - rob_chr->set_osage_reset(); - } break; - case DSC_FT_MIKU_ROT: { - a1->chara_id = (int32_t)data[0]; - pv_play_data* playdata = &a1->playdata[a1->chara_id]; - - float_t rot_y = (float_t)(int32_t)data[0] * 0.001f; - - rob_chara* rob_chr = playdata->rob_chr; - if (!rob_chr) - break; - - int16_t rot_y_int16 = (int32_t)((rot_y + a1->scene_rot_y) * 32768.0f * (float_t)(1.0 / 180.0)); - rob_chr->data.miku_rot.rot_y_int16 = rot_y_int16; - rob_chr->set_osage_reset(); - } break; - case DSC_FT_MIKU_DISP: { - data_struct* aft_data = &data_list[DATA_AFT]; - bone_database* aft_bone_data = &aft_data->data_ft.bone_data; - motion_database* aft_mot_db = &aft_data->data_ft.mot_db; - - a1->chara_id = (int32_t)data[0]; - pv_play_data* playdata = &a1->playdata[a1->chara_id]; - - int32_t disp = (int32_t)data[1]; - - playdata->disp = disp == 1; - - rob_chara* rob_chr = playdata->rob_chr; - if (!rob_chr) - break; - - if (disp == 1) { - rob_chr->set_visibility(true); - /*if (rob_chara::check_for_ageageagain_module(chara_index, module_index)) { - sub_1405430F0(chara_id, 1); - sub_1405430F0(chara_id, 2); - }*/ - - //pv_game_old::set_data_itmpv_visibility(a1->pv_game_old, a1->chara_id, true); - for (pv_play_data_set_motion& i : playdata->set_motion) { - bool v45 = rob_chr->set_motion_id(i.motion_id, i.frame, - i.duration, i.field_10, 0, i.blend_type, aft_bone_data, aft_mot_db); - rob_chr->set_motion_reset_data(i.motion_id, i.dsc_frame); - rob_chr->bone_data->disable_eye_motion = i.disable_eye_motion; - rob_chr->data.motion.step_data.step = i.frame_speed; - if (v45) - pv_expression_array_set_motion(hash_murmurhash_empty, a1->chara_id, i.motion_id); - //if (!a1->pv_game_old->data.pv->disable_calc_motfrm_limit) - pv_game_dsc_data_set_motion_max_frame(a1, a1->chara_id, i.motion_index, i.dsc_time); - } - playdata->set_motion.clear(); - } - else { - rob_chr->set_visibility(false); - //pv_game_old::set_data_itmpv_visibility(a1->pv_game_old, a1->chara_id, false); - } - } break; - case DSC_FT_MIKU_SHADOW: { - a1->chara_id = data[0]; - } break; - case DSC_FT_TARGET: { - - } break; - case DSC_FT_SET_MOTION: { - data_struct* aft_data = &data_list[DATA_AFT]; - bone_database* aft_bone_data = &aft_data->data_ft.bone_data; - motion_database* aft_mot_db = &aft_data->data_ft.mot_db; - - a1->chara_id = (int32_t)data[0]; - pv_play_data* playdata = &a1->playdata[a1->chara_id]; - - rob_chara* rob_chr = playdata->rob_chr; - if (!rob_chr) - break; - - int32_t motion_index = (int32_t)data[1]; - int32_t duration_int = (int32_t)data[2]; - int32_t frame_speed_int = (int32_t)data[3]; - - float_t duration; - if (duration_int != -1) { - duration = (float_t)duration_int * 0.001f * 60.0f; - if (duration < 0.0f) - duration = 0.0f; - } - else - duration = 0.0f; - - float_t frame_speed; - if (frame_speed_int != -1) { - frame_speed = (float_t)frame_speed_int * 0.001f; - if (frame_speed < 0.0f) - frame_speed = 0.0f; - } - else - frame_speed = 1.0f; - - float_t frame = 0.0f; - float_t dsc_frame = 0.0f; - - bool v11 = false; - pv_play_data_motion* v56 = 0; - for (pv_play_data_motion& i : playdata->motion) - if (i.motion_index == motion_index) { - v56 = &i; - break; - } - - if (v56 && v56->enable && (motion_index == v56->motion_index || !v56->motion_index)) { - if (a1->dsc_time != v56->time) { - frame = dsc_time_to_frame(curr_time - v56->time); - dsc_frame = roundf(dsc_time_to_frame(a1->dsc_time - v56->time)); - if (frame < dsc_frame) - frame = dsc_frame; - v11 = curr_time > v56->time; - } - else - frame = 0.0f; - } - - int32_t motion_id = -1; - int32_t chara_id = 0; - /*pv_db_pv_difficulty* v64 = 0i64; - pv_db_pv* v65 = a1->pv_game->data.pv; - pv_db_pv_difficulty* v66 = v65->difficulty[sub_14013C8C0()->difficulty].begin; - pv_db_pv* v67 = a1->pv_game->data.pv; - if (v66 == v67->difficulty[sub_14013C8C0()->difficulty].end) - return 1; - do { - if (v66->edition == sub_14013C8C0()->edition) - v64 = v66; - ++v66; - pv_db_pv* v68 = a1->pv_game->data.pv; - } while (v66 != v68->difficulty[sub_14013C8C0()->difficulty].end); - if (!v64) - return 1; - - int32_t motion_id = vector_pv_db_pv_motion_get_element_by_index_or_null(&v64->motion[a1->chara_id], v52)->id; - if (pv_game_data_get()->data.pv) { - pv_db_pv_motion* v76 = vector_pv_db_pv_motion_get_element_by_index_or_null(&v64->motion[a1->chara_id], v52); - motion_id = pv_game_data_get()->data.pv->get_chrmot_motion_id(ob_chr->chara_id, rob_chr->chara_index, v76); - }*/ - - if (motion_index) { - if (motion_id == -1) - motion_id = rob_chr->get_rob_cmn_mottbl_motion_id(0); - } - else if (motion_id == -1) { - motion_id = rob_chr->get_rob_cmn_mottbl_motion_id(5); - if (motion_id == -1) - motion_id = rob_chr->get_rob_cmn_mottbl_motion_id(0); - } - - duration /= a1->anim_frame_speed; - if (v11) - duration = 0.0f; - - pv_play_data_set_motion v519; - v519.frame_speed = frame_speed * a1->anim_frame_speed; - v519.motion_id = motion_id; - v519.frame = frame; - v519.duration = duration; - v519.field_10 = v11; - v519.blend_type = MOTION_BLEND_CROSS; - v519.disable_eye_motion = true; - v519.motion_index = motion_index; - v519.dsc_time = v56 ? v56->time : a1->dsc_time; - v519.dsc_frame = dsc_frame; - - //a1->field_2C560[a1->chara_id] = true; - //a1->field_2C568[a1->chara_id] = v519; - if (playdata->disp) { - bool v84 = rob_chr->set_motion_id(motion_id, frame, duration, - v11, false, MOTION_BLEND_CROSS, aft_bone_data, aft_mot_db); - rob_chr->set_motion_reset_data(motion_id, dsc_frame); - rob_chr->set_motion_skin_param(motion_id, dsc_frame); - rob_chr->bone_data->disable_eye_motion = true; - rob_chr->data.motion.step_data.step = v519.frame_speed; - if (v84) - pv_expression_array_set_motion(hash_murmurhash_empty, a1->chara_id, motion_id); - //if (!a1->pv_game_old->data.pv->disable_calc_motfrm_limit) - pv_game_dsc_data_set_motion_max_frame(a1, a1->chara_id, motion_index, v56 ? v56->time : 0); - } - else { - playdata->set_motion.clear(); - playdata->set_motion.push_back(v519); - rob_chr->data.motion.step_data.step = v519.frame_speed; - } - } break; - case DSC_FT_SET_PLAYDATA: { - a1->chara_id = (int32_t)data[0]; - pv_play_data* playdata = &a1->playdata[a1->chara_id]; - - int32_t motion_index = (int32_t)data[1]; - if (motion_index < 0) { - playdata->motion.clear(); - break; - } - - pv_play_data_motion* v92 = 0; - for (pv_play_data_motion& i : playdata->motion) - if (i.motion_index == motion_index) { - v92 = &i; - break; - } - - if (v92) { - v92->enable = true; - v92->motion_index = motion_index; - v92->time = a1->dsc_time; - } - else { - pv_play_data_motion v531; - v531.enable = true; - v531.motion_index = motion_index; - v531.time = a1->dsc_time; - playdata->motion.push_back(v531); - } - } break; - case DSC_FT_EFFECT: { - - } break; - case DSC_FT_FADEIN_FIELD: { - - } break; - case DSC_FT_EFFECT_OFF: { - - } break; - case DSC_FT_SET_CAMERA: { - - } break; - case DSC_FT_DATA_CAMERA: { - - } break; - case DSC_FT_CHANGE_FIELD: { - int32_t field = (int32_t)data[0]; - if (field > 0) - pv_game_change_field(a1, field, a1->dsc_time, curr_time); - else - pv_game_reset_field(a1); - } break; - case DSC_FT_HIDE_FIELD: { - - } break; - case DSC_FT_MOVE_FIELD: { - - } break; - case DSC_FT_FADEOUT_FIELD: { - - } break; - case DSC_FT_EYE_ANIM: { - data_struct* aft_data = &data_list[DATA_AFT]; - motion_database* aft_mot_db = &aft_data->data_ft.mot_db; - - a1->chara_id = (int32_t)data[0]; - pv_play_data* playdata = &a1->playdata[a1->chara_id]; - - int32_t v115 = (int32_t)data[1]; - int32_t duration_int = (int32_t)data[2]; - - float_t duration; - if (duration_int != -1) - duration = (float_t)duration_int * 0.001f * 60.0f; - else - duration = 6.0f; - - rob_chara* rob_chr = playdata->rob_chr; - if (!rob_chr) - break; - - duration /= a1->anim_frame_speed; - - float_t offset = 0.0f; - //if (a1->pv_game_old->data.pv->is_old_pv) - // offset = 1.0f; - - rob_chr->set_eyelid_mottbl_motion_from_face(v115, duration, -1.0f, offset, aft_mot_db); - } break; - case DSC_FT_MOUTH_ANIM: { - data_struct* aft_data = &data_list[DATA_AFT]; - motion_database* aft_mot_db = &aft_data->data_ft.mot_db; - - a1->chara_id = (int32_t)data[0]; - pv_play_data* playdata = &a1->playdata[a1->chara_id]; - - int32_t mouth_anim_id = (int32_t)data[2]; - int32_t duration_int = (int32_t)data[3]; - int32_t value_int = (int32_t)data[4]; - - float_t duration; - if (duration_int != -1) { - duration = (float_t)duration_int * 0.001f * 60.0f; - if (duration < 0.0f) - duration = 0.0f; - } - else - duration = 6.0f; - - float_t value; - if (value_int != -1) { - value = (float_t)value_int * 0.001f; - if (value < 0.0f || value > 1.0f) - value = 1.0f; - } - else - value = 1.0f; - - rob_chara* rob_chr = playdata->rob_chr; - if (!rob_chr) - break; - - int32_t mottbl_index = mouth_anim_id_to_mottbl_index(mouth_anim_id); - duration /= a1->anim_frame_speed; - - //if (a1->pv_game_old->data.field_2D090 && mottbl_index != 144) - // value = 0.0f; - - float_t offset = 0.0f; - //if (a1->pv_game_old->data.pv->is_old_pv) - // offset = 1.0f; - - rob_chr->set_mouth_mottbl_motion(0, mottbl_index, value, - 0, duration, 0.0f, 1.0f, -1, offset, aft_mot_db); - } break; - case DSC_FT_HAND_ANIM: { - data_struct* aft_data = &data_list[DATA_AFT]; - motion_database* aft_mot_db = &aft_data->data_ft.mot_db; - - a1->chara_id = (int32_t)data[0]; - pv_play_data* playdata = &a1->playdata[a1->chara_id]; - - int32_t hand_index = (int32_t)data[1]; - int32_t hand_anim_id = (int32_t)data[2]; - int32_t duration_int = (int32_t)data[3]; - int32_t value_int = (int32_t)data[4]; - - float_t duration; - if (duration_int != -1) { - duration = (float_t)duration_int * 0.001f * 60.0f; - if (duration < 0.0f) - duration = 0.0f; - } - else - duration = 0.0f; - - float_t value; - if (value_int != -1) { - value = (float_t)value_int * 0.001f; - if (value < 0.0f || value > 1.0f) - value = 1.0f; - } - else - value = 1.0f; - - rob_chara* rob_chr = playdata->rob_chr; - if (!rob_chr) - break; - - int32_t mottbl_index = hand_anim_id_to_mottbl_index(hand_anim_id); - duration /= a1->anim_frame_speed; - - float_t offset = 0.0f; - //if (a1->pv_game_old->data.pv->is_old_pv) - // offset = 1.0f; - - switch (hand_index) { - case 0: - rob_chr->set_hand_l_mottbl_motion(0, mottbl_index, value, - 0, duration, 0.0f, 1.0f, -1, offset, aft_mot_db); - break; - case 1: - rob_chr->set_hand_r_mottbl_motion(0, mottbl_index, value, - 0, duration, 0.0f, 1.0f, -1, offset, aft_mot_db); - break; - } - } break; - case DSC_FT_LOOK_ANIM: { - data_struct* aft_data = &data_list[DATA_AFT]; - motion_database* aft_mot_db = &aft_data->data_ft.mot_db; - - a1->chara_id = (int32_t)data[0]; - pv_play_data* playdata = &a1->playdata[a1->chara_id]; - - int32_t look_anim_id = (int32_t)data[1]; - int32_t duration_int = (int32_t)data[2]; - int32_t value_int = (int32_t)data[3]; - - float_t duration; - if (duration_int != -1) { - duration = (float_t)duration_int * 0.001f * 60.0f; - if (duration < 0.0f) - duration = 0.0f; - } - else - duration = 6.0f; - - float_t value; - if (value_int != -1) { - value = (float_t)value_int * 0.001f; - if (value < 0.0f || value > 1.0f) - value = 1.0f; - } - else - value = 1.0f; - - rob_chara* rob_chr = playdata->rob_chr; - if (!rob_chr) - break; - - int32_t mottbl_index = look_anim_id_to_mottbl_index(look_anim_id); - duration /= a1->anim_frame_speed; - - float_t offset = 0.0f; - //if (a1->pv_game_old->data.pv->is_old_pv) - // offset = 1.0f; - - rob_chr->set_eyes_mottbl_motion(0, mottbl_index, value, - mottbl_index == 224 ? 1 : 0, duration, 0.0f, 1.0f, -1, offset, aft_mot_db); - return 1; - } break; - case DSC_FT_EXPRESSION: { - data_struct* aft_data = &data_list[DATA_AFT]; - motion_database* aft_mot_db = &aft_data->data_ft.mot_db; - - a1->chara_id = (int32_t)data[0]; - pv_play_data* playdata = &a1->playdata[a1->chara_id]; - - int32_t expression_id = (int32_t)data[1]; - int32_t duration_int = (int32_t)data[2]; - int32_t value_int = (int32_t)data[3]; - - float_t duration; - if (duration_int != -1) { - duration = (float_t)duration_int * 0.001f * 60.0f; - if (duration < 0.0f) - duration = 0.0f; - } - else - duration = 0.0f; - - float_t value; - if (value_int != -1) { - value = (float_t)value_int * 0.001f; - if (value < 0.0f || value > 1.0f) - value = 1.0f; - } - else - value = 1.0f; - - rob_chara* rob_chr = playdata->rob_chr; - if (!rob_chr) - break; - - int32_t mottbl_index = expression_id_to_mottbl_index(expression_id); - duration /= a1->anim_frame_speed; - - bool v168 = true; - //if (a1->has_perf_id && (a1->pv_game_old->data.pv->edit - 1) <= 1) - // v168 = false; - - float_t offset = 0.0f; - //if (a1->pv_game_old->data.pv->is_old_pv) - // offset = 1.0f; - - rob_chr->set_face_mottbl_motion(0, mottbl_index, value, mottbl_index >= 214 - && mottbl_index <= 223 ? 1 : 0, duration, 0.0f, 1.0f, -1, offset, v168, aft_mot_db); - } break; - case DSC_FT_LOOK_CAMERA: { - - } break; - case DSC_FT_LYRIC: { - - } break; - case DSC_FT_MUSIC_PLAY: { - - } break; - case DSC_FT_MODE_SELECT: { - - } break; - case DSC_FT_EDIT_MOTION: { - - } break; - case DSC_FT_BAR_TIME_SET: { - int32_t bpm = (int32_t)data[0]; - } break; - case DSC_FT_SHADOWHEIGHT: { - a1->chara_id = data[0]; - } break; - case DSC_FT_EDIT_FACE: - case DSC_FT_EDIT_EXPRESSION: { - data_struct* aft_data = &data_list[DATA_AFT]; - motion_database* aft_mot_db = &aft_data->data_ft.mot_db; - - pv_play_data* playdata = &a1->playdata[a1->chara_id]; - - int32_t expression_id = (int32_t)data[0]; - - float_t duration = 0.0f; - if (func == DSC_FT_EDIT_EXPRESSION) - duration = (float_t)(int32_t)data[1] * 0.001f * 60.0f; - - int32_t v234 = -1; - //if (!a1->has_perf_id) - // v234 = data[1]; - - rob_chara* rob_chr = playdata->rob_chr; - if (!rob_chr) - break; - - int32_t mottbl_index = expression_id_to_mottbl_index(expression_id); - duration /= a1->anim_frame_speed; - - bool v237 = true; - //if (a1->has_perf_id) - // v237 = (a1->pv_game_old->data.pv->edit - 1) > 1; - - float_t offset = 0.0f; - //if (a1->pv_game_old->data.pv->is_old_pv) - // offset = 1.0f; - - rob_chr->set_face_mottbl_motion(0, mottbl_index, 1.0f, mottbl_index >= 214 - && mottbl_index <= 223 ? 1 : 0, duration, 0.0f, 1.0f, -1, offset, v237, aft_mot_db); - - /*if (!a1->has_perf_id) { - int32_t mottbl_index = mouth_anim_id_to_mottbl_index(v234); - - float_t value = 1.0f; - //if (a1->pv_game_old->data.field_2D090 && mottbl_index != 144) - // value = 0.0f; - - rob_chr->set_mouth_mottbl_motion(0, mottbl_index, value, 0, - a1->target_anim_fps * 0.1f, 0.0f, 1.0f, -1, offset, aft_mot_db); - }*/ - } break; - case DSC_FT_MOVE_CAMERA: { - - } break; - case DSC_FT_PV_END: { - a1->pv_end = true; - } break; - case DSC_FT_SHADOWPOS: { - a1->chara_id = (int32_t)data[0]; - } break; - case DSC_FT_EDIT_LYRIC: { - - } break; - case DSC_FT_EDIT_TARGET: { - - } break; - case DSC_FT_EDIT_MOUTH: { - data_struct* aft_data = &data_list[DATA_AFT]; - motion_database* aft_mot_db = &aft_data->data_ft.mot_db; - - a1->chara_id = (int32_t)data[0]; - pv_play_data* playdata = &a1->playdata[a1->chara_id]; - - int32_t mouth_anim_id = (int32_t)data[1]; - float_t duration = 0.1f; - //if (func == DSC_EDIT_MOUTH_ANIM) - // duration = (float_t)(int32_t)data[2] * 0.001f; - - rob_chara* rob_chr = playdata->rob_chr; - if (!rob_chr) - break; - - int32_t mottbl_index = mouth_anim_id_to_mottbl_index(mouth_anim_id); - duration *= a1->target_anim_fps; - - float_t value = 1.0f; - //if (a1->pv_game_old->data.field_2D090 && mottbl_index != 144) - // value = 0.0f; - - float_t offset = 0.0f; - //if (a1->pv_game_old->data.pv->is_old_pv) - // offset = 1.0f; - - rob_chr->set_mouth_mottbl_motion(0, mottbl_index, value, - 0, duration, 0.0f, 1.0f, -1, offset, aft_mot_db); - } break; - case DSC_FT_SET_CHARA: { - a1->chara_id = (int32_t)data[0]; - } break; - case DSC_FT_EDIT_MOVE: { - - } break; - case DSC_FT_EDIT_SHADOW: { - - } break; - case DSC_FT_EDIT_EYELID: { - - } break; - case DSC_FT_EDIT_EYE: { - - } break; - case DSC_FT_EDIT_ITEM: { - - } break; - case DSC_FT_EDIT_EFFECT: { - - } break; - case DSC_FT_EDIT_DISP: { - - } break; - case DSC_FT_EDIT_HAND_ANIM: { - - } break; - case DSC_FT_AIM: { - a1->chara_id = (int32_t)data[0]; - } break; - case DSC_FT_HAND_ITEM: { - - } break; - case DSC_FT_EDIT_BLUSH: { - - } break; - case DSC_FT_NEAR_CLIP: { - - } break; - case DSC_FT_CLOTH_WET: { - data_struct* aft_data = &data_list[DATA_AFT]; - motion_database* aft_mot_db = &aft_data->data_ft.mot_db; - - a1->chara_id = (int32_t)data[0]; - pv_play_data* playdata = &a1->playdata[a1->chara_id]; - - float_t value = (float_t)(int32_t)data[1] * 0.001f; - - rob_chara* rob_chr = playdata->rob_chr; - if (!rob_chr) - break; - - rob_chr->item_equip->wet = clamp_def(value, 0.0f, 1.0f); - } break; - case DSC_FT_LIGHT_ROT: { - - } break; - case DSC_FT_SCENE_FADE: { - - } break; - case DSC_FT_TONE_TRANS: { - vec3 start; - vec3 end; - start.x = (float_t)(int32_t)data[0] * 0.001f; - start.y = (float_t)(int32_t)data[1] * 0.001f; - start.z = (float_t)(int32_t)data[2] * 0.001f; - end.x = (float_t)(int32_t)data[3] * 0.001f; - end.y = (float_t)(int32_t)data[4] * 0.001f; - end.z = (float_t)(int32_t)data[5] * 0.001f; - rctx_ptr->post_process.tone_map->set_tone_trans(start, end); - } break; - case DSC_FT_SATURATE: { - float_t value = (float_t)(int32_t)data[0] * 0.001f; - rctx_ptr->post_process.tone_map->set_saturate_coeff(value); - } break; - case DSC_FT_FADE_MODE: { - int32_t value = data[0]; - rctx_ptr->post_process.tone_map->set_scene_fade_blend_func(value); - } break; - case DSC_FT_AUTO_BLINK: { - - } break; - case DSC_FT_PARTS_DISP: { - data_struct* aft_data = &data_list[DATA_AFT]; - motion_database* aft_mot_db = &aft_data->data_ft.mot_db; - - a1->chara_id = (int32_t)data[0]; - pv_play_data* playdata = &a1->playdata[a1->chara_id]; - - item_id id = (item_id)data[1]; - int32_t disp = (int32_t)data[2]; - - rob_chara* rob_chr = playdata->rob_chr; - if (!rob_chr) - break; - - rob_chr->set_parts_disp(id, disp == 1); - } break; - case DSC_FT_TARGET_FLYING_TIME: { - - } break; - case DSC_FT_CHARA_SIZE: { - a1->chara_id = (int32_t)data[0]; - pv_play_data* playdata = &a1->playdata[a1->chara_id]; - - int32_t chara_size = (int32_t)data[1]; - - rob_chara* rob_chr = playdata->rob_chr; - if (!rob_chr) - return 1; - - int32_t chara_size_index; - if (chara_size == 0) - chara_size_index = chara_init_data_get_chara_size_index(rob_chr->chara_index); - else if (chara_size == 1) { - chara_index chara_index = CHARA_MIKU; - //if (a1->chara_id < a1->pp->chara.size()) - // chara_index = (::chara_index)a1->pp->chara[a1->chara_id].chara_effect.base_chara; - - //chara_index chara_index = pv_db_pv::get_performer_chara(a1->pv_game_old->data.pv, a1->chara_id); - chara_size_index = chara_init_data_get_chara_size_index(chara_index); - } - else if (chara_size == 2) - chara_size_index = 1; - else if (chara_size == 3) - chara_size_index = rob_chr->pv_data.chara_size_index; - else { - rob_chr->set_chara_size((float_t)chara_size / 1000.0f/*a1->field_2C54C*/); - break; - } - - if (chara_size_index < 0 || chara_size_index > 4) - break; - - rob_chr->set_chara_size(chara_size_table_get_value(chara_size_index)); - rob_chr->set_chara_pos_adjust_y(chara_pos_adjust_y_table_get_value(chara_size_index)); - } break; - case DSC_FT_CHARA_HEIGHT_ADJUST: { - a1->chara_id = (int32_t)data[0]; - pv_play_data* playdata = &a1->playdata[a1->chara_id]; - - int32_t height_adjust = (int32_t)data[1]; - - rob_chara* rob_chr = playdata->rob_chr; - if (rob_chr) - rob_chr->set_chara_height_adjust(height_adjust != 0); - } break; - case DSC_FT_ITEM_ANIM: { - - } break; - case DSC_FT_CHARA_POS_ADJUST: { - a1->chara_id = (int32_t)data[0]; - pv_play_data* playdata = &a1->playdata[a1->chara_id]; - - vec3 pos; - pos.x = (float_t)(int32_t)data[2] * 0.001f; - pos.y = (float_t)(int32_t)data[3] * 0.001f; - pos.z = (float_t)(int32_t)data[4] * 0.001f; - - rob_chara* rob_chr = playdata->rob_chr; - if (!rob_chr) - break; - - mat4_mult_vec3_trans(&a1->scene_rot_mat, &pos, &pos); - rob_chr->set_chara_pos_adjust(&pos); - - /*if (rob_chara_check_for_ageageagain_module(rob_chr->chara_index, rob_chr->module_index)) { - sub_1405430F0(rob_chr->chara_id, 1); - sub_1405430F0(rob_chr->chara_id, 2); - }*/ - } break; - case DSC_FT_SCENE_ROT: { - float_t scene_rot_y = (float_t)(int32_t)data[0] * 0.001f; - a1->scene_rot_y = scene_rot_y; - - mat4_rotate_y(scene_rot_y * DEG_TO_RAD_FLOAT, &a1->scene_rot_mat); - } break; - case DSC_FT_EDIT_MOT_SMOOTH_LEN: { - - } break; - case DSC_FT_PV_BRANCH_MODE: { - int32_t branch_mode = (int32_t)data[0]; - if (branch_mode >= 0 && branch_mode <= 2) - a1->branch_mode = branch_mode; - } break; - case DSC_FT_DATA_CAMERA_START: { - - } break; - case DSC_FT_MOVIE_PLAY: { - - } break; - case DSC_FT_MOVIE_DISP: { - - } break; - case DSC_FT_WIND: { - data_struct* aft_data = &data_list[DATA_AFT]; - motion_database* aft_mot_db = &aft_data->data_ft.mot_db; - - a1->chara_id = (int32_t)data[0]; - pv_play_data* playdata = &a1->playdata[a1->chara_id]; - - float_t wind_strength_kami = (float_t)(int32_t)data[1] * 0.001f; - float_t wind_strength_outer = (float_t)(int32_t)data[2] * 0.001f; - - rob_chara* rob_chr = playdata->rob_chr; - if (!rob_chr) - break; - - rob_chr->item_equip->wind_strength = wind_strength_outer; - } break; - case DSC_FT_OSAGE_STEP: { - data_struct* aft_data = &data_list[DATA_AFT]; - motion_database* aft_mot_db = &aft_data->data_ft.mot_db; - - a1->chara_id = (int32_t)data[0]; - pv_play_data* playdata = &a1->playdata[a1->chara_id]; - - float_t osage_step_kami = (float_t)(int32_t)data[1] * 0.001f; - float_t osage_step_outer = (float_t)(int32_t)data[2] * 0.001f; - - rob_chara* rob_chr = playdata->rob_chr; - if (!rob_chr) - break; - - rob_chr->set_osage_step(osage_step_outer); - } break; - case DSC_FT_OSAGE_MV_CCL: { - data_struct* aft_data = &data_list[DATA_AFT]; - motion_database* aft_mot_db = &aft_data->data_ft.mot_db; - - a1->chara_id = (int32_t)data[0]; - pv_play_data* playdata = &a1->playdata[a1->chara_id]; - - float_t osage_mv_ccl_kami = (float_t)(int32_t)data[1] * 0.001f; - float_t osage_mv_ccl_outer = (float_t)(int32_t)data[2] * 0.001f; - - rob_chara* rob_chr = playdata->rob_chr; - if (!rob_chr) - break; - - rob_chr->set_osage_move_cancel(1, osage_mv_ccl_kami); - rob_chr->set_osage_move_cancel(2, osage_mv_ccl_outer); - } break; - case DSC_FT_CHARA_COLOR: { - - } break; - case DSC_FT_SE_EFFECT: { - - } break; - case DSC_FT_EDIT_MOVE_XYZ: { - - } break; - case DSC_FT_EDIT_EYELID_ANIM: { - - } break; - case DSC_FT_EDIT_INSTRUMENT_ITEM: { - - } break; - case DSC_FT_EDIT_MOTION_LOOP: { - - } break; - case DSC_FT_EDIT_EYE_ANIM: { - - } break; - case DSC_FT_EDIT_MOUTH_ANIM: { - - } break; - case DSC_FT_EDIT_CAMERA: { - - } break; - case DSC_FT_EDIT_MODE_SELECT: { - - } break; - case DSC_FT_PV_END_FADEOUT: { - - } break; - case DSC_FT_TARGET_FLAG: { - - } break; - case DSC_FT_ITEM_ANIM_ATTACH: { - - } break; - case DSC_FT_SHADOW_RANGE: { - - } break; - case DSC_FT_HAND_SCALE: { - - } break; - case DSC_FT_LIGHT_POS: { - - } break; - case DSC_FT_FACE_TYPE: { - - } break; - case DSC_FT_SHADOW_CAST: { - - } break; - case DSC_FT_EDIT_MOTION_F: { - - } break; - case DSC_FT_FOG: { - - } break; - case DSC_FT_BLOOM: { - - } break; - case DSC_FT_COLOR_COLLE: { - - } break; - case DSC_FT_DOF: { - - } break; - case DSC_FT_CHARA_ALPHA: { - a1->chara_id = (int32_t)data[0]; - - float_t alpha = (float_t)(int32_t)data[1] * 0.001f; - float_t duration = (float_t)(int32_t)data[2]; - int32_t type = (int32_t)data[3]; - - if (a1->chara_id >= 0 && a1->chara_id < ROB_CHARA_COUNT) { - pv_param::chara_alpha& chara_alpha - = pv_param_task::post_process_task.chara_alpha.data.data[a1->chara_id]; - chara_alpha.type = type; - chara_alpha.frame = 0.0f; - chara_alpha.alpha = alpha; - chara_alpha.duration = duration; - } - } break; - case DSC_FT_AOTO_CAP: { - - } break; - case DSC_FT_MAN_CAP: { - - } break; - case DSC_FT_TOON: { - - } break; - case DSC_FT_SHIMMER: { - - } break; - case DSC_FT_ITEM_ALPHA: { - a1->chara_id = (int32_t)data[0]; - - float_t alpha = (float_t)(int32_t)data[1] * 0.001f; - float_t duration = (float_t)(int32_t)data[2]; - int32_t type = (int32_t)data[3]; - - if (a1->chara_id >= 0 && a1->chara_id < ROB_CHARA_COUNT) { - pv_param::chara_alpha& chara_item_alpha - = pv_param_task::post_process_task.chara_item_alpha.data.data[a1->chara_id]; - chara_item_alpha.type = type; - chara_item_alpha.frame = 0.0f; - chara_item_alpha.alpha = alpha; - chara_item_alpha.duration = duration; - } - } break; - case DSC_FT_MOVIE_CUT_CHG: { - - } break; - case DSC_FT_CHARA_LIGHT: { - - } break; - case DSC_FT_STAGE_LIGHT: { - - } break; - case DSC_FT_AGEAGE_CTRL: { - - } break; - } - return true; -} - -static void pv_game_reset_field(pv_game_old* pvgm) { - task_stage_info v14; - task_stage_set_stage(&v14); - Glitter::glt_particle_manager->FreeScenes(); -} - -static dsc_data* pv_game_dsc_data_find_func(pv_game_old* a1, dsc_ft_func func_name, - int32_t* time, int32_t* pv_branch_mode, dsc_data* start, dsc_data* end) { - int32_t _time = -1; - for (dsc_data* i = start; i != end; i++) - if (i->func == DSC_FT_END) - break; - else if (i->func == DSC_FT_TIME) { - uint32_t* data = a1->dsc_m.get_func_data(i); - _time = (int32_t)data[0]; - } - else if (i->func == DSC_FT_PV_END) - break; - else if (i->func == DSC_FT_PV_BRANCH_MODE) { - if (pv_branch_mode) { - uint32_t* data = a1->dsc_m.get_func_data(i); - *pv_branch_mode = (int32_t)data[0]; - } - } - else if (i->func == func_name) { - if (time) - *time = _time; - return i; - } - return 0; -} - -static void pv_game_dsc_data_find_playdata_item_anim(pv_game_old* a1, int32_t chara_id) { - if (chara_id < 0 || chara_id >= ROB_CHARA_COUNT) - return; - - int32_t pv_branch_mode = 0; - int32_t time = -1; - int32_t prev_time = -1; - a1->playdata[chara_id].motion_data.set_item.clear(); - - dsc_data* i = a1->dsc_m.data.data(); - dsc_data* i_end = a1->dsc_m.data.data() + a1->dsc_m.data.size(); - while(true) { - i = pv_game_dsc_data_find_func(a1, DSC_FT_ITEM_ANIM, &time, &pv_branch_mode, i, i_end); - if (!i) - break; - - if (time < 0) { - time = prev_time; - if (prev_time < 0) - break; - } - - uint32_t* data = a1->dsc_m.get_func_data(i); - if (chara_id == data[0] && data[2] == 2) { - dsc_set_item v14; - v14.item_index = data[1]; - v14.time = time; - v14.pv_branch_mode = pv_branch_mode; - a1->playdata[chara_id].motion_data.set_item.push_back(v14); - } - prev_time = time; - i++; - } -} - -static void pv_game_dsc_data_find_playdata_set_motion(pv_game_old* pvgm, int32_t chara_id) { - if (chara_id < 0 || chara_id >= ROB_CHARA_COUNT) - return; - - int32_t pv_branch_mode = 0; - int32_t time = -1; - int32_t prev_time = -1; - pvgm->playdata[chara_id].motion_data.set_motion.clear(); - - dsc_data* i = pvgm->dsc_m.data.data(); - dsc_data* i_end = pvgm->dsc_m.data.data() + pvgm->dsc_m.data.size(); - while (true) { - i = pv_game_dsc_data_find_func(pvgm, DSC_FT_SET_MOTION, &time, &pv_branch_mode, i, i_end); - if (!i) - break; - - if (time < 0) { - time = prev_time; - if (prev_time < 0) - break; - } - - uint32_t* data = pvgm->dsc_m.get_func_data(i); - if (chara_id == data[0]) { - dsc_set_motion v14; - v14.motion_index = data[1]; - v14.time = time; - v14.pv_branch_mode = pv_branch_mode; - pvgm->playdata[chara_id].motion_data.set_motion.push_back(v14); - } - prev_time = time; - i++; - } -} - -static void pv_game_dsc_data_set_motion_max_frame(pv_game_old* pvgm, - int32_t chara_id, int32_t motion_index, int64_t disp_time) { - if (chara_id < 0 || chara_id > ROB_CHARA_COUNT || motion_index < 0) - return; - - pv_play_data* playdata = &pvgm->playdata[chara_id]; - rob_chara_bone_data* rob_bone_data = playdata->rob_chr->bone_data; - int64_t dsc_time = pvgm->dsc_time; - for (dsc_set_motion& i : playdata->motion_data.set_motion) { - int64_t time = (int64_t)i.time * 10000; - if (time > dsc_time && i.motion_index == motion_index - && (!pvgm->branch_mode || pvgm->branch_mode == i.pv_branch_mode)) { - playdata->rob_chr->bone_data->set_motion_max_frame( - roundf(dsc_time_to_frame(time - disp_time)) - 1.0f); - break; - } - } -} -#endif diff --git a/src/ReDIVA/pv_game.hpp b/src/ReDIVA/pv_game.hpp index 9b511443..951b6671 100644 --- a/src/ReDIVA/pv_game.hpp +++ b/src/ReDIVA/pv_game.hpp @@ -593,61 +593,6 @@ public: virtual void Disp() override; }; -class pv_game_old : public app::TaskWindow { -public: - int32_t pv_id; - - std::vector objset_load; - - int32_t state; - int32_t frame; - double_t frame_float; - int64_t time; - int32_t rob_chara_ids[ROB_CHARA_COUNT]; - - std::string pv_category; - std::string stage_category; - std::string camera_category; - - std::vector category_load; - - std::map pv_auth_3d_ids; - auth_3d_id light_auth_3d_id; - auth_3d_id camera_auth_3d_id; - - dsc dsc_m; - dsc_data* dsc_data_ptr; - dsc_data* dsc_data_ptr_end; - - bool play; - bool success; - int32_t chara_id; - float_t target_anim_fps; - float_t anim_frame_speed; - int64_t dsc_time; - bool pv_end; - pv_play_data playdata[ROB_CHARA_COUNT]; - float_t scene_rot_y; - mat4 scene_rot_mat; - int32_t branch_mode; - - bool pause; - bool step_frame; - - pv_game_old(); - virtual ~pv_game_old() override; - - virtual bool Init() override; - virtual bool Ctrl() override; - virtual bool Dest() override; - virtual void Disp() override; - virtual void Basic() override; - virtual void Window() override; - - void Load(int32_t pv_id, chara_index charas[6], int32_t modules[6]); - void Unload(); -}; - -bool task_pv_game_append_task(TaskPvGame::InitData* init_data); +bool task_pv_game_add_task(TaskPvGame::InitData* init_data); bool task_pv_game_check_task_ready(); -bool task_pv_game_free_task(); +bool task_pv_game_del_task(); diff --git a/src/ReDIVA/render.cpp b/src/ReDIVA/render.cpp index 2b9e3cda..b0a8c47c 100644 --- a/src/ReDIVA/render.cpp +++ b/src/ReDIVA/render.cpp @@ -1,14 +1,25 @@ /* by korenkonder GitHub/GitLab: korenkonder - - Some code is from LearnOpenGL */ +#define GLFW_INCLUDE_VULKAN +#define GLFW_EXPOSE_NATIVE_WIN32 +#define VMA_IMPLEMENTATION +#define VMA_VULKAN_VERSION 1000000 #include "render.hpp" #include "config.hpp" +#include "../CRE/GL/uniform_buffer.hpp" #include "../CRE/Glitter/glitter.hpp" #include "../CRE/rob/rob.hpp" +#include "../CRE/rob/motion.hpp" +#include "../CRE/rob/skin_param.hpp" +#include "../CRE/Vulkan/command_buffer.hpp" +#include "../CRE/Vulkan/fence.hpp" +#include "../CRE/Vulkan/framebuffer.hpp" +#include "../CRE/Vulkan/image.hpp" +#include "../CRE/Vulkan/image_view.hpp" +#include "../CRE/Vulkan/semaphore.hpp" #include "../CRE/camera.hpp" #include "../CRE/clear_color.hpp" #include "../CRE/data.hpp" @@ -25,7 +36,6 @@ #include "../CRE/random.hpp" #include "../CRE/shader.hpp" #include "../CRE/shader_ft.hpp" -#include "../CRE/shader_glsl.hpp" #include "../CRE/stage.hpp" #include "../CRE/stage_modern.hpp" #include "../CRE/stage_param.hpp" @@ -33,9 +43,9 @@ #include "../CRE/task.hpp" #include "../CRE/task_effect.hpp" #include "../CRE/texture.hpp" -#include "../CRE/timer.hpp" #include "../CRE/post_process.hpp" #include "../KKdLib/database/item_table.hpp" +#include "../KKdLib/timer.hpp" #include "../KKdLib/sort.hpp" #include "../KKdLib/str_utils.hpp" #include "data_test/auth_3d_test.hpp" @@ -48,43 +58,152 @@ #include "pv_game.hpp" #include "x_pv_game.hpp" #include +#include +#include #include -#define GLFW_EXPOSE_NATIVE_WIN32 #include #include #include #include #include +#include #include #if defined(DEBUG) -#define OPENGL_DEBUG 0 +#define RENDER_DEBUG 1 #endif #define CUBE_LINE_SIZE (0.0025f) #define CUBE_LINE_POINT_SIZE (CUBE_LINE_SIZE * 1.5f) -#define DRAW_PASS_TIME_DISP 0 - -shader_glsl* cube_line_shader; -shader_glsl* cube_line_point_shader; -shader_glsl* grid_shader; - timer* render_timer; #define grid_size 50.0f #define grid_spacing 1.0f size_t grid_vertex_count = ((size_t)(grid_size / grid_spacing) * 2 + 1) * 4; -GLuint cube_line_vao; -GLuint cube_line_vbo; -GLuint cube_line_point_vao; -GLuint cube_line_point_instance_vbo; -GLuint grid_vao; -GLuint grid_vbo; -static GLuint common_data_ubo = 0; +struct common_data_struct { + vec4 res; //x=width, y=height, z=1/width, w=1/height + mat4 vp; + mat4 view; + mat4 projection; + vec3 view_pos; +}; -#define COMMON_DATA_SIZE (int32_t)(sizeof(vec4) + sizeof(mat4) * 3 + sizeof(vec4)) +struct render_opengl_data { + GLuint grid_vao; + GLuint grid_vbo; + GL::UniformBuffer common_data_ubo; + + render_opengl_data(); + ~render_opengl_data(); + + bool load(); + void unload(); + + void load_common_data(); + void unload_common_data(); + void update_common_data(common_data_struct* common_data, camera* cam); +}; + +struct render_vulkan_data { + struct swapchain_support_details { + VkSurfaceCapabilitiesKHR capabilities; + std::vector formats; + std::vector present_modes; + + swapchain_support_details(); + ~swapchain_support_details(); + }; + + struct pipeline_builder_data { + VkPipelineShaderStageCreateInfo shader_stages[2]; + VkPipelineVertexInputStateCreateInfo vertex_input; + VkPipelineInputAssemblyStateCreateInfo input_assembly; + VkPipelineRasterizationStateCreateInfo rasterizer; + VkPipelineColorBlendAttachmentState color_blend_attachment; + VkPipelineMultisampleStateCreateInfo multisampling; + VkPipelineLayout pipeline_layout; + VkPipelineDepthStencilStateCreateInfo depth_stencil; + + pipeline_builder_data(); + + VkResult build_pipeline(VkDevice device, VkRenderPass render_pass, VkPipeline& pipeline); + }; + + VkInstance instance; +#if RENDER_DEBUG + VkDebugUtilsMessengerEXT debug_messenger; +#endif + VkSurfaceKHR surface; + + VkPhysicalDevice physical_device; + VkDevice device; + + VkQueue graphics_queue; + uint32_t graphics_queue_family; + + VkQueue present_queue; + uint32_t present_queue_family; + + VkSwapchainKHR swapchain; + VkExtent2D swapchain_extent; + VkFormat swapchain_image_format; + std::vector swapchain_images; + std::vector swapchain_image_views; + + VkRenderPass render_pass; + + std::vector swapchain_framebuffers; + + VkCommandPool command_pool; + Vulkan::CommandBuffer main_command_buffer; + + Vulkan::Semaphore present_semaphore; + Vulkan::Semaphore render_semaphore; + Vulkan::Fence render_fence; + + VmaAllocator allocator; + + pipeline_builder_data pipeline_builder; + + VkPipelineLayout triangle_pipeline_layout; + + VkPipeline triangle_pipeline; + + render_vulkan_data(); + ~render_vulkan_data(); + + bool load(); + void unload(); + + void load_common_data(); + void unload_common_data(); + void update_common_data(common_data_struct* common_data, camera* cam); + + static bool check_device_extension_support(VkPhysicalDevice device); + static VkExtent2D choose_swap_extent(const VkSurfaceCapabilitiesKHR& capabilities); + static VkPresentModeKHR choose_swap_present_mode( + const std::vector& available_present_modes); + static VkSurfaceFormatKHR choose_swap_surface_format( + const std::vector& available_formats); + static std::pair find_queue_families( + VkPhysicalDevice device, VkSurfaceKHR surface); + static bool is_device_suitable(VkPhysicalDevice device, VkSurfaceKHR surface); + static swapchain_support_details query_swapchain_support( + VkPhysicalDevice device, VkSurfaceKHR surface); + +#if RENDER_DEBUG + static VKAPI_ATTR VkBool32 VKAPI_CALL debug_callback( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData); +#endif +}; + +common_data_struct common_data; + +render_opengl_data* render_opengl; +render_vulkan_data* render_vulkan; bool draw_imgui = true; bool draw_grid_3d = false; @@ -107,6 +226,30 @@ const double_t render_scale_table[] = { 8.0 / 4.0, // 200% }; +#if RENDER_DEBUG +static const char* render_vulkan_validation_layers[] = { + "VK_LAYER_KHRONOS_validation", +}; +#endif + +static const char* render_vulkan_device_extensions[] = { + VK_KHR_SWAPCHAIN_EXTENSION_NAME, +}; + +#if defined(DEBUG) +#if defined(ReDIVA_DEV) +static const char* application_name = "ReDIVADev Debug"; +#else +static const char* application_name = "ReDIVA Debug"; +#endif +#else +#if defined(ReDIVA_DEV) +static const char* application_name = "ReDIVADev"; +#else +static const char* application_name = "ReDIVA"; +#endif +#endif + static int32_t old_scale_index; static int32_t scale_index; @@ -122,24 +265,28 @@ int32_t height; static const double_t aspect = 16.0 / 9.0; bool light_chara_ambient; -vec4 npr_spec_color; +vec4 npr_cloth_spec_color; uint32_t cmn_set_id; uint32_t dbg_set_id; -#if DRAW_PASS_TIME_DISP -static std::vector draw_pass_cpu_time[DRAW_PASS_MAX]; -static std::vector draw_pass_gpu_time[DRAW_PASS_MAX]; -#endif +static bool render_init(render_init_struct* ris); +static void render_main_loop(render_context* rctx); +static void render_free(); -static render_context* render_load(); -static void render_ctrl(render_context* rctx); -static void render_disp(render_context* rctx); -static void render_dispose(render_context* rctx); +static render_context* render_context_load(); +static void render_context_ctrl(render_context* rctx); +static void render_context_disp(render_context* rctx); +static void render_context_imgui(render_context* rctx); +static void render_context_dispose(render_context* rctx); -static bool render_load_shaders(void* data, const char* path, const char* file, uint32_t hash); +static void render_opengl_shaders_load(); +static void render_opengl_shaders_free(); +static void render_vulkan_shaders_load(); +static void render_vulkan_shaders_free(); -static void render_imgui(render_context* rctx); +static bool render_opengl_load_shaders(void* data, const char* path, const char* file, uint32_t hash); +static bool render_vulkan_load_shaders(void* data, const char* path, const char* file, uint32_t hash); static void render_drop_glfw(GLFWwindow* window, int32_t count, char** paths); static void render_resize_fb_glfw(GLFWwindow* window, int32_t w, int32_t h); @@ -147,12 +294,16 @@ static void render_resize_fb(render_context* rctx, bool change_fb); static void render_imgui_context_menu(classes_data* classes, const size_t classes_count, render_context* rctx); -static void render_shaders_load(); -static void render_shaders_free(); -#if defined(DEBUG) && OPENGL_DEBUG +#if RENDER_DEBUG static void APIENTRY render_debug_output(GLenum source, GLenum type, uint32_t id, GLenum severity, GLsizei length, const char* message, const void* userParam); + +static VkResult vkGetInstanceProcAddrCreateDebugUtilsMessengerEXT(VkInstance instance, + const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger); +static void vkGetInstanceProcAddrDestroyDebugUtilsMessengerEXT(VkInstance instance, + VkDebugUtilsMessengerEXT messenger, const VkAllocationCallbacks* pAllocator); #endif bool close; @@ -166,15 +317,9 @@ bool global_context_menu; extern size_t frame_counter; render_context* rctx_ptr; bool task_stage_is_modern; - -render_init_struct::render_init_struct() : scale_index() { - -} +bool vulkan_render; int32_t render_main(render_init_struct* ris) { - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); - timeBeginPeriod(1); - render_lock = new lock_cs; if (!render_lock) return 0; @@ -183,198 +328,87 @@ int32_t render_main(render_init_struct* ris) { window_handle = 0; -#pragma region GLFW Init if (!glfwInit()) return -1; - GLFWmonitor* monitor = glfwGetPrimaryMonitor(); - const GLFWvidmode* mode = glfwGetVideoMode(monitor); - width = ris->res.x > 0 && ris->res.x < 8192 ? ris->res.x : mode->width; - height = ris->res.y > 0 && ris->res.y < 8192 ? ris->res.y : mode->height; - - width = (int32_t)(width / 2.0f); - height = (int32_t)(height / 2.0f); - + if (render_init(ris)) { + window_handle = glfwGetWin32Window(window); + glfwShowWindow(window); + glfwFocusWindow(window); + glfwSetDropCallback(window, (GLFWdropfun)render_drop_glfw); + glfwSetWindowSizeCallback(window, (GLFWwindowsizefun)render_resize_fb_glfw); + glfwSetWindowSize(window, width, height); + glfwSetWindowSizeLimits(window, 896, 504, GLFW_DONT_CARE, GLFW_DONT_CARE); #if BAKE_PNG || BAKE_VIDEO - width = 1920; - height = 1080; -#endif - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); - glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE); - //glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); -#if defined(DEBUG) && OPENGL_DEBUG - glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, true); -#endif - - glfwWindowHint(GLFW_RED_BITS, mode->redBits); - glfwWindowHint(GLFW_GREEN_BITS, mode->greenBits); - glfwWindowHint(GLFW_BLUE_BITS, mode->blueBits); - glfwWindowHint(GLFW_DEPTH_BITS, 0); - -#if BAKE_PNG || BAKE_VIDEO - bool maximized = false; + glfwSetWindowPos(window, 0, 0); + glfwSetWindowAttrib(window, GLFW_DECORATED, GLFW_FALSE); #else - bool maximized = mode->width == width && mode->height == height; -#endif - glfwWindowHint(GLFW_MAXIMIZED, maximized ? GLFW_TRUE : GLFW_FALSE); - - int32_t minor = 6; - window = 0; - while (!window || minor < 3) { - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, minor--); - - const char* glfw_titlelabel; -#if defined(DEBUG) -#if defined(ReDIVA_DEV) - glfw_titlelabel = "ReDIVADev Debug"; -#else - glfw_titlelabel = "ReDIVA Debug"; -#endif -#else -#if defined(ReDIVA_DEV) - glfw_titlelabel = "ReDIVADev"; -#else - glfw_titlelabel = "ReDIVA"; -#endif -#endif - window = glfwCreateWindow(width, height, glfw_titlelabel, maximized ? monitor : 0, 0); - } - - if (!window) { - glfwTerminate(); - return -2; - } - - glfwMakeContextCurrent(window); - - if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) { - glfwDestroyWindow(window); - glfwTerminate(); - return -3; - } - - glGetError(); - glViewport(0, 0, width, height); - - window_handle = glfwGetWin32Window(window); - glfwShowWindow(window); - glfwFocusWindow(window); - glfwSetDropCallback(window, (GLFWdropfun)render_drop_glfw); - glfwSetWindowSizeCallback(window, (GLFWwindowsizefun)render_resize_fb_glfw); - glfwSetWindowSize(window, width, height); - glfwSetWindowSizeLimits(window, 896, 504, GLFW_DONT_CARE, GLFW_DONT_CARE); -#if BAKE_PNG || BAKE_VIDEO - glfwSetWindowPos(window, 0, 0); - glfwSetWindowAttrib(window, GLFW_DECORATED, GLFW_FALSE); -#else - glfwSetWindowPos(window, 8, 31); + glfwSetWindowPos(window, 8, 31); #endif - Input::SetInputs(window); + Input::SetInputs(window); - RECT window_rect; - GetClientRect(window_handle, &window_rect); + RECT window_rect; + GetClientRect(window_handle, &window_rect); #if !(BAKE_PNG || BAKE_VIDEO) - width = window_rect.right; - height = window_rect.bottom; + width = window_rect.right; + height = window_rect.bottom; #endif - scale_index = ris->scale_index > 0 && ris->scale_index < RENDER_SCALE_MAX - ? ris->scale_index : RENDER_SCALE_100; - old_scale_index = scale_index; -#pragma endregion + scale_index = ris->scale_index > 0 && ris->scale_index < RENDER_SCALE_MAX + ? ris->scale_index : RENDER_SCALE_100; + old_scale_index = scale_index; -#if defined(DEBUG) && OPENGL_DEBUG - glEnable(GL_DEBUG_OUTPUT); - glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); - glDebugMessageCallback(render_debug_output, 0); - glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, 0, GL_TRUE); + if (!vulkan_render) { +#if RENDER_DEBUG + glEnable(GL_DEBUG_OUTPUT); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + glDebugMessageCallback(render_debug_output, 0); + glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, 0, GL_TRUE); #endif - - glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &sv_max_texture_buffer_size); - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &sv_max_texture_size); - glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &sv_max_texture_max_anisotropy); - glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); - glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); - -#if DRAW_PASS_TIME_DISP - for (std::vector& i : draw_pass_cpu_time) - i.resize(240, 0.0f); - - for (std::vector& i : draw_pass_gpu_time) - i.resize(240, 0.0f); -#endif - - do { - close = false; - reload_render = false; - - render_context* rctx = 0; - lock_lock(render_lock); - rctx = render_load(); - lock_unlock(render_lock); - - //sound_work_play_stream(1, "rom/sound/bgm/selector_verB_a_lp.ogg"); - - frame_counter = 0; - -#pragma region GL Init -#if !(BAKE_PNG || BAKE_VIDEO) - glfwGetFramebufferSize(window, &width, &height); -#endif - glViewport(0, 0, width, height); - glfwSwapInterval(0); - - gl_state_disable_blend(); - gl_state_disable_depth_test(); - gl_state_set_depth_mask(GL_FALSE); - gl_state_disable_cull_face(); - gl_state_disable_stencil_test(); -#pragma endregion - render_timer->reset(); - while (!close && !reload_render) { - render_timer->start_of_cycle(); - glfwPollEvents(); - ImGui_ImplGlfw_NewFrame(); - ImGui_ImplOpenGL3_NewFrame(); - ImGui::NewFrame(); - Input::NewFrame(); - lock_lock(render_lock); - render_ctrl(rctx); - lock_unlock(render_lock); - render_disp(rctx); - close |= !!glfwWindowShouldClose(window); - frame_counter++; - Input::EndFrame(); - glfwSwapBuffers(window); - render_timer->end_of_cycle(); + glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &sv_max_texture_buffer_size); + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &sv_max_texture_size); + glGetIntegerv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &sv_max_texture_max_anisotropy); + glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); + glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); } - //sound_work_release_stream(1); + do { + close = false; + reload_render = false; - lock_lock(render_lock); - render_dispose(rctx); - lock_unlock(render_lock); - } while (reload_render); + render_context* rctx = 0; + lock_lock(render_lock); + rctx = render_context_load(); + lock_unlock(render_lock); -#if DRAW_PASS_TIME_DISP - for (std::vector& i : draw_pass_cpu_time) { - i.clear(); - i.shrink_to_fit(); - } + frame_counter = 0; - for (std::vector& i : draw_pass_gpu_time) { - i.clear(); - i.shrink_to_fit(); - } +#if !(BAKE_PNG || BAKE_VIDEO) + glfwGetFramebufferSize(window, &width, &height); #endif + if (!vulkan_render) { + glViewport(0, 0, width, height); + gl_state_disable_blend(); + gl_state_disable_depth_test(); + gl_state_set_depth_mask(GL_FALSE); + gl_state_disable_cull_face(); + gl_state_disable_stencil_test(); + } -#pragma region GLFW Dispose - glfwDestroyWindow(window); + glfwSwapInterval(0); + + render_main_loop(rctx); + + lock_lock(render_lock); + render_context_dispose(rctx); + lock_unlock(render_lock); + } while (reload_render); + } + + render_free(); glfwTerminate(); -#pragma endregion + delete render_lock; render_lock = 0; delete render_timer; @@ -394,9 +428,890 @@ void render_set_scale_index(int32_t index) { scale_index = index >= 0 && index < RENDER_SCALE_MAX ? index : RENDER_SCALE_100; } +void draw_pass_3d_grid(render_context* rctx) { + rctx->camera->update_data(); + + if (!vulkan_render) { + gl_state_enable_blend(); + gl_state_set_blend_func(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + gl_state_set_blend_equation(GL_FUNC_ADD); + gl_state_enable_depth_test(); + gl_state_set_depth_mask(GL_TRUE); + + shaders_ft.set_opengl_shader(SHADER_FT_GRID); + render_opengl->common_data_ubo.Bind(0); + gl_state_bind_vertex_array(render_opengl->grid_vao); + glDrawArrays(GL_LINES, 0, (GLsizei)grid_vertex_count); + gl_state_use_program(0); + + gl_state_disable_depth_test(); + gl_state_set_depth_mask(GL_FALSE); + gl_state_disable_blend(); + } +} + float_t rob_frame = 0.0f; -static render_context* render_load() { +render_opengl_data::render_opengl_data() : grid_vao(), grid_vbo(), common_data_ubo() { + +} + +render_opengl_data::~render_opengl_data() { + +} + +bool render_opengl_data::load() { + glfwMakeContextCurrent(window); + + if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)) + return false; + + glGetError(); + glViewport(0, 0, width, height); + return true; +} + +void render_opengl_data::unload() { + +} + +void render_opengl_data::load_common_data() { + common_data_ubo.Create(sizeof(common_data_struct)); + + float_t* grid_verts = force_malloc_s(float_t, 3 * grid_vertex_count); + + size_t v = 0; + for (float_t x = -grid_size; x <= grid_size; x += grid_spacing) { + int32_t x_color_index; + int32_t z_color_index; + if (x == 0) { + x_color_index = 0; + z_color_index = 1; + } + else { + x_color_index = 2; + z_color_index = 2; + } + + grid_verts[v++] = x; + grid_verts[v++] = -grid_size; + *(int32_t*)&grid_verts[v++] = x_color_index; + + grid_verts[v++] = x; + grid_verts[v++] = grid_size; + *(int32_t*)&grid_verts[v++] = x_color_index; + + grid_verts[v++] = -grid_size; + grid_verts[v++] = x; + *(int32_t*)&grid_verts[v++] = z_color_index; + + grid_verts[v++] = grid_size; + grid_verts[v++] = x; + *(int32_t*)&grid_verts[v++] = z_color_index; + } + + glGenVertexArrays(1, &grid_vao); + gl_state_bind_vertex_array(grid_vao); + + glGenBuffers(1, &grid_vbo); + gl_state_bind_array_buffer(grid_vbo); + if (GLAD_GL_VERSION_4_4) + glBufferStorage(GL_ARRAY_BUFFER, sizeof(float_t) * 3 + * grid_vertex_count, grid_verts, 0); + else + glBufferData(GL_ARRAY_BUFFER, sizeof(float_t) * 3 + * grid_vertex_count, grid_verts, GL_STATIC_DRAW); + + glVertexAttrib4f(0, 0.0f, 0.0f, 0.0f, 1.0f); + glVertexAttribI1i(1, 2); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, + sizeof(float_t) * 3, (void*)0); // Pos + glEnableVertexAttribArray(1); + glVertexAttribIPointer(1, 1, GL_INT, + sizeof(float_t) * 3, (void*)(sizeof(float_t) * 2)); // Color + + gl_state_bind_array_buffer(0); + gl_state_bind_vertex_array(0); + + free(grid_verts); +} + +void render_opengl_data::unload_common_data() { + common_data_ubo.Destroy(); + + if (grid_vbo) { + glDeleteBuffers(1, &grid_vbo); + grid_vbo = 0; + } + + if (grid_vao) { + glDeleteVertexArrays(1, &grid_vao); + grid_vao = 0; + } +} + +void render_opengl_data::update_common_data(common_data_struct* common_data, camera* cam) { + common_data_ubo.WriteMapMemory(*common_data); +} + +render_vulkan_data::swapchain_support_details::swapchain_support_details() : capabilities() { + +} + +render_vulkan_data::swapchain_support_details::~swapchain_support_details() { + +} + +render_vulkan_data::pipeline_builder_data::pipeline_builder_data() : shader_stages(), +vertex_input(), input_assembly(), rasterizer(), color_blend_attachment(), +multisampling(), pipeline_layout(), depth_stencil() { + +} + +VkResult render_vulkan_data::pipeline_builder_data::build_pipeline(VkDevice device, + VkRenderPass render_pass, VkPipeline& pipeline) { + VkPipelineViewportStateCreateInfo viewport_state_create_info = {}; + viewport_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; + viewport_state_create_info.viewportCount = 1; + viewport_state_create_info.scissorCount = 1; + + VkPipelineColorBlendStateCreateInfo color_blending_create_info = {}; + color_blending_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; + color_blending_create_info.logicOpEnable = VK_FALSE; + color_blending_create_info.logicOp = VK_LOGIC_OP_COPY; + color_blending_create_info.attachmentCount = 1; + color_blending_create_info.pAttachments = &color_blend_attachment; + + const VkDynamicState dynamic_states[] = { + VK_DYNAMIC_STATE_VIEWPORT, + VK_DYNAMIC_STATE_SCISSOR, + }; + + VkPipelineDynamicStateCreateInfo dynamic_state_create_info = {}; + dynamic_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; + dynamic_state_create_info.dynamicStateCount = 2; + dynamic_state_create_info.pDynamicStates = dynamic_states; + + VkGraphicsPipelineCreateInfo pipeline_info_create_info = {}; + pipeline_info_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; + pipeline_info_create_info.stageCount = 2; + pipeline_info_create_info.pStages = shader_stages; + pipeline_info_create_info.pVertexInputState = &vertex_input; + pipeline_info_create_info.pInputAssemblyState = &input_assembly; + pipeline_info_create_info.pViewportState = &viewport_state_create_info; + pipeline_info_create_info.pRasterizationState = &rasterizer; + pipeline_info_create_info.pMultisampleState = &multisampling; + pipeline_info_create_info.pDepthStencilState = &depth_stencil; + pipeline_info_create_info.pColorBlendState = &color_blending_create_info; + pipeline_info_create_info.pDynamicState = &dynamic_state_create_info; + pipeline_info_create_info.layout = pipeline_layout; + pipeline_info_create_info.renderPass = render_pass; + + return vkCreateGraphicsPipelines(device, 0, 1, &pipeline_info_create_info, 0, &pipeline); +} + +render_vulkan_data::render_vulkan_data() : instance(), surface(), physical_device(), device(), +graphics_queue(), present_queue(), swapchain(), swapchain_extent(), swapchain_image_format(), +render_pass(), command_pool(), triangle_pipeline_layout(), triangle_pipeline(), allocator() { +#if RENDER_DEBUG + debug_messenger = 0; +#endif + graphics_queue_family = -1; + present_queue_family = -1; +} + +render_vulkan_data::~render_vulkan_data() { + +} + +bool render_vulkan_data::load() { +#pragma region Vulkan Validation Layers Support +#if RENDER_DEBUG + uint32_t layer_count; + vkEnumerateInstanceLayerProperties(&layer_count, 0); + + std::vector available_layers; + available_layers.resize(layer_count); + vkEnumerateInstanceLayerProperties(&layer_count, available_layers.data()); + + for (const char* i : render_vulkan_validation_layers) { + bool found = false; + for (const VkLayerProperties& j : available_layers) + if (!strcmp(i, j.layerName)) { + found = true; + break; + } + + if (!found) + return false; + } +#endif +#pragma endregion + +#pragma region Vulkan Instance + VkApplicationInfo application_info = {}; + application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; + application_info.pApplicationName = application_name; + application_info.applicationVersion = VK_MAKE_API_VERSION(0, 1, 0, 0); + application_info.engineVersion = VK_MAKE_API_VERSION(0, 1, 0, 0); + application_info.apiVersion = VK_API_VERSION_1_0; + + VkInstanceCreateInfo instance_create_info = {}; + instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instance_create_info.pApplicationInfo = &application_info; + + uint32_t glfw_extension_count = 0; + const char** glfw_extensions = glfwGetRequiredInstanceExtensions(&glfw_extension_count); + + std::vector extensions(glfw_extensions, glfw_extensions + glfw_extension_count); +#if RENDER_DEBUG + extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); +#endif + + instance_create_info.enabledExtensionCount = (uint32_t)extensions.size(); + instance_create_info.ppEnabledExtensionNames = extensions.data(); + +#if RENDER_DEBUG + instance_create_info.enabledLayerCount = (uint32_t)( + sizeof(render_vulkan_validation_layers) / sizeof(const char*)); + instance_create_info.ppEnabledLayerNames = render_vulkan_validation_layers; + + VkDebugUtilsMessengerCreateInfoEXT debug_create_info = {}; + debug_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + debug_create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT + | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT + | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + debug_create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT + | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT + | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + debug_create_info.pfnUserCallback = render_vulkan_data::debug_callback; + + instance_create_info.pNext = &debug_create_info; +#else + instance_create_info.enabledLayerCount = 0; + instance_create_info.pNext = 0; +#endif + + if (vkCreateInstance(&instance_create_info, 0, &instance) != VK_SUCCESS) + return false; +#pragma endregion + +#pragma region Vulkan Debug +#if RENDER_DEBUG + if (vkGetInstanceProcAddrCreateDebugUtilsMessengerEXT(instance, + &debug_create_info, 0, &debug_messenger) != VK_SUCCESS) + return false; +#endif +#pragma endregion + +#pragma region Vulkan Surface + if (glfwCreateWindowSurface(instance, window, 0, &surface) != VK_SUCCESS) + return false; +#pragma endregion + +#pragma region Vulkan Physical Device + uint32_t device_count = 0; + vkEnumeratePhysicalDevices(instance, &device_count, 0); + + if (!device_count) + return false; + + std::vector devices; + devices.resize(device_count); + vkEnumeratePhysicalDevices(instance, &device_count, devices.data()); + + for (const VkPhysicalDevice& i : devices) + if (render_vulkan_data::is_device_suitable(i, surface)) { + physical_device = i; + break; + } + + if (!physical_device) + return false; +#pragma endregion + +#pragma region Vulkan Device + std::pair indices = find_queue_families(physical_device, surface); + if (indices.first == -1 || indices.second == -1) + return false; + + std::set unique_queue_families; + unique_queue_families.insert(indices.first); + unique_queue_families.insert(indices.second); + + std::vector queue_create_infos; + queue_create_infos.reserve(unique_queue_families.size()); + + float_t queue_priority = 1.0f; + for (uint32_t i : unique_queue_families) { + VkDeviceQueueCreateInfo queue_create_info = {}; + queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queue_create_info.queueFamilyIndex = i; + queue_create_info.queueCount = 1; + queue_create_info.pQueuePriorities = &queue_priority; + queue_create_infos.push_back(queue_create_info); + } + + VkPhysicalDeviceFeatures device_features = {}; + device_features.samplerAnisotropy = VK_TRUE; + + VkDeviceCreateInfo device_create_info = {}; + device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + + device_create_info.queueCreateInfoCount = (uint32_t)queue_create_infos.size(); + device_create_info.pQueueCreateInfos = queue_create_infos.data(); + + device_create_info.pEnabledFeatures = &device_features; + + device_create_info.enabledExtensionCount = (uint32_t)(sizeof(render_vulkan_device_extensions) / sizeof(const char*)); + device_create_info.ppEnabledExtensionNames = render_vulkan_device_extensions; + +#if RENDER_DEBUG + device_create_info.enabledLayerCount = (uint32_t)( + sizeof(render_vulkan_validation_layers) / sizeof(const char*)); + device_create_info.ppEnabledLayerNames = render_vulkan_validation_layers; +#else + device_create_info.enabledLayerCount = 0; +#endif + + if (vkCreateDevice(physical_device, &device_create_info, 0, &device) != VK_SUCCESS) + return false; + + graphics_queue_family = indices.first; + vkGetDeviceQueue(device, indices.first, 0, &graphics_queue); + + present_queue_family = indices.second; + vkGetDeviceQueue(device, indices.second, 0, &present_queue); +#pragma endregion + +#pragma region Vulkan Swapchain + swapchain_support_details swapchain_support = query_swapchain_support(physical_device, surface); + + VkSurfaceFormatKHR surface_format = choose_swap_surface_format(swapchain_support.formats); + VkPresentModeKHR present_mode = choose_swap_present_mode(swapchain_support.present_modes); + VkExtent2D extent = choose_swap_extent(swapchain_support.capabilities); + + uint32_t image_count = swapchain_support.capabilities.minImageCount + 1; + if (swapchain_support.capabilities.maxImageCount > 0 + && image_count > swapchain_support.capabilities.maxImageCount) + image_count = swapchain_support.capabilities.maxImageCount; + + VkSwapchainCreateInfoKHR swapchain_create_info = {}; + swapchain_create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapchain_create_info.surface = surface; + + swapchain_create_info.minImageCount = image_count; + swapchain_create_info.imageFormat = surface_format.format; + swapchain_create_info.imageColorSpace = surface_format.colorSpace; + swapchain_create_info.imageExtent = extent; + swapchain_create_info.imageArrayLayers = 1; + swapchain_create_info.imageUsage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + + uint32_t queue_family_indices[2]; + queue_family_indices[0] = graphics_queue_family; + queue_family_indices[1] = present_queue_family; + + if (graphics_queue_family != present_queue_family) { + swapchain_create_info.imageSharingMode = VK_SHARING_MODE_CONCURRENT; + swapchain_create_info.queueFamilyIndexCount = sizeof(queue_family_indices) / sizeof(uint32_t); + swapchain_create_info.pQueueFamilyIndices = queue_family_indices; + } + else + swapchain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; + + swapchain_create_info.preTransform = swapchain_support.capabilities.currentTransform; + swapchain_create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; + swapchain_create_info.presentMode = present_mode; + swapchain_create_info.clipped = VK_TRUE; + + swapchain_create_info.oldSwapchain = 0; + + if (vkCreateSwapchainKHR(device, &swapchain_create_info, 0, &swapchain) != VK_SUCCESS) + return false; + + vkGetSwapchainImagesKHR(device, swapchain, &image_count, 0); + + swapchain_images.resize(image_count); + vkGetSwapchainImagesKHR(device, swapchain, &image_count, swapchain_images.data()); + + swapchain_image_format = surface_format.format; + swapchain_extent = extent; + + size_t swapchain_image_count = swapchain_images.size(); + swapchain_image_views.resize(swapchain_image_count); + + VkComponentMapping components; + components.r = VK_COMPONENT_SWIZZLE_IDENTITY; + components.g = VK_COMPONENT_SWIZZLE_IDENTITY; + components.b = VK_COMPONENT_SWIZZLE_IDENTITY; + components.a = VK_COMPONENT_SWIZZLE_IDENTITY; + + VkImageSubresourceRange sub_resource_range; + sub_resource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + sub_resource_range.baseMipLevel = 0; + sub_resource_range.levelCount = 1; + sub_resource_range.baseArrayLayer = 0; + sub_resource_range.layerCount = 1; + + for (size_t i = 0; i < swapchain_image_count; i++) + if (!swapchain_image_views[i].Create(device, 0, swapchain_images[i], + VK_IMAGE_VIEW_TYPE_2D, swapchain_image_format, components, sub_resource_range)) + return false; +#pragma endregion + +#pragma region Vulkan Render Pass + VkAttachmentDescription color_attachment = {}; + color_attachment.format = swapchain_image_format; + color_attachment.samples = VK_SAMPLE_COUNT_1_BIT; + color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; + color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; + color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + color_attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; + + VkAttachmentReference color_attachment_ref = {}; + color_attachment_ref.attachment = 0; + color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + + VkSubpassDescription subpass_description = {}; + subpass_description.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; + subpass_description.colorAttachmentCount = 1; + subpass_description.pColorAttachments = &color_attachment_ref; + + VkRenderPassCreateInfo render_pass_create_info = {}; + render_pass_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; + render_pass_create_info.attachmentCount = 1; + render_pass_create_info.pAttachments = &color_attachment; + render_pass_create_info.subpassCount = 1; + render_pass_create_info.pSubpasses = &subpass_description; + + if (vkCreateRenderPass(device, &render_pass_create_info, 0, &render_pass) != VK_SUCCESS) + return false; +#pragma endregion + +#pragma region Vulkan Framebuffers + size_t swapchain_image_view_count = swapchain_image_views.size(); + swapchain_framebuffers.resize(swapchain_image_view_count); + + for (size_t i = 0; i < swapchain_image_view_count; i++) + if (!swapchain_framebuffers[i].Create(device, 0, render_pass, 1, + &swapchain_image_views[i].data, swapchain_extent.width, swapchain_extent.height, 1)) + return false; +#pragma endregion + +#pragma region Vulkan Command Pool + VkCommandPoolCreateInfo command_pool_create_info = {}; + command_pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + command_pool_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + command_pool_create_info.queueFamilyIndex = graphics_queue_family; + + if (vkCreateCommandPool(device, &command_pool_create_info, nullptr, &command_pool) != VK_SUCCESS) + return false; +#pragma endregion + +#pragma region Vulkan Main Command Buffer + if (main_command_buffer.Allocate(device, command_pool)) + return false; +#pragma endregion + +#pragma region Vulkan Sync + if (!present_semaphore.Create(device) + || !render_semaphore.Create(device) + || !render_fence.Create(device, VK_FENCE_CREATE_SIGNALED_BIT)) + return false; +#pragma endregion + +#pragma region Vulkan Allocator + VmaAllocatorCreateInfo allocator_create_info = {}; + allocator_create_info.vulkanApiVersion = VK_API_VERSION_1_0; + allocator_create_info.physicalDevice = physical_device; + allocator_create_info.device = device; + allocator_create_info.instance = instance; + + if (vmaCreateAllocator(&allocator_create_info, &allocator) != VK_SUCCESS) + return false; +#pragma endregion + return true; +} + +void render_vulkan_data::unload() { + vkDeviceWaitIdle(device); + +#pragma region Vulkan Allocator + if (allocator) { + vmaDestroyAllocator(allocator); + allocator = 0; + } +#pragma endregion + +#pragma region Vulkan Sync + render_fence.Destroy(); + render_semaphore.Destroy(); + present_semaphore.Destroy(); +#pragma endregion + +#pragma region Vulkan Main Command Buffer + main_command_buffer.Free(); +#pragma endregion + +#pragma region Vulkan Command Pool + if (command_pool) { + vkDestroyCommandPool(device, command_pool, 0); + command_pool = 0; + } +#pragma endregion + +#pragma region Vulkan Framebuffers + for (Vulkan::Framebuffer& i : swapchain_framebuffers) + i.Destroy(); + swapchain_framebuffers.clear(); +#pragma endregion + +#pragma region Vulkan Render Pass + if (render_pass) { + vkDestroyRenderPass(device, render_pass, 0); + render_pass = 0; + } +#pragma endregion + +#pragma region Vulkan Swapchain + for (Vulkan::ImageView& i : swapchain_image_views) + i.Destroy(); + + swapchain_images.clear(); + + if (swapchain) { + vkDestroySwapchainKHR(device, swapchain, 0); + swapchain = 0; + } +#pragma endregion + +#pragma region Vulkan Device + present_queue_family = -1; + present_queue = 0; + + graphics_queue_family = -1; + graphics_queue = 0; + + if (device) { + vkDestroyDevice(device, 0); + device = 0; + } +#pragma endregion + +#pragma region Vulkan Physical Device + physical_device = 0; +#pragma endregion + +#pragma region Vulkan Surface + if (surface) { + vkDestroySurfaceKHR(instance, surface, 0); + surface = 0; + } +#pragma endregion + +#pragma region Vulkan Debug +#if RENDER_DEBUG + if (debug_messenger) { + vkGetInstanceProcAddrDestroyDebugUtilsMessengerEXT(instance, debug_messenger, 0); + debug_messenger = 0; + } +#endif +#pragma endregion + +#pragma region Vulkan Instance + if (instance) { + vkDestroyInstance(instance, 0); + instance = 0; + } +#pragma endregion +} + +void render_vulkan_data::load_common_data() { + +} + +void render_vulkan_data::unload_common_data() { + +} + +void render_vulkan_data::update_common_data(common_data_struct* common_data, camera* cam) { + +} + +bool render_vulkan_data::check_device_extension_support(VkPhysicalDevice device) { + uint32_t extension_count = 0; + vkEnumerateDeviceExtensionProperties(device, 0, &extension_count, 0); + + if (!extension_count) + return true; + + std::vector available_extensions; + available_extensions.resize(extension_count); + vkEnumerateDeviceExtensionProperties(device, 0, &extension_count, available_extensions.data()); + + std::set required_extensions(render_vulkan_device_extensions, + render_vulkan_device_extensions + sizeof(render_vulkan_device_extensions) / sizeof(const char*)); + for (const VkExtensionProperties& i : available_extensions) + required_extensions.erase(i.extensionName); + return !required_extensions.size(); +} + +VkExtent2D render_vulkan_data::choose_swap_extent(const VkSurfaceCapabilitiesKHR& capabilities) { + if (capabilities.currentExtent.width != UINT32_MAX) + return capabilities.currentExtent; + + int32_t width, height; + glfwGetFramebufferSize(window, &width, &height); + + VkExtent2D actual_extent{ (uint32_t)width, (uint32_t)height, }; + + actual_extent.width = clamp_def(actual_extent.width, + capabilities.minImageExtent.width, capabilities.maxImageExtent.width); + actual_extent.height = clamp_def(actual_extent.height, + capabilities.minImageExtent.height, capabilities.maxImageExtent.height); + + return actual_extent; +} + +VkPresentModeKHR render_vulkan_data::choose_swap_present_mode( + const std::vector& available_present_modes) { + for (const VkPresentModeKHR& i : available_present_modes) // It'll uncap framerates + if (i == VK_PRESENT_MODE_MAILBOX_KHR) + return i; + return VK_PRESENT_MODE_FIFO_KHR; +} + +VkSurfaceFormatKHR render_vulkan_data::choose_swap_surface_format( + const std::vector& available_formats) { + for (const VkSurfaceFormatKHR& i : available_formats) + if (i.format == VK_FORMAT_B8G8R8A8_UNORM + && i.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) + return i; + return available_formats[0]; +} + +std::pair render_vulkan_data::find_queue_families( + VkPhysicalDevice device, VkSurfaceKHR surface) { + uint32_t queue_family_count = 0; + vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, 0); + + std::vector queue_families; + queue_families.resize(queue_family_count); + vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, queue_families.data()); + + int32_t j = 0; + std::pair indices = { -1, -1 }; + for (const VkQueueFamilyProperties& i : queue_families) { + if (i.queueFlags & VK_QUEUE_GRAPHICS_BIT) + indices.first = j; + + VkBool32 present_support = false; + vkGetPhysicalDeviceSurfaceSupportKHR(device, j, surface, &present_support); + + if (present_support) + indices.second = j; + + if (indices.first != -1 && indices.second != -1) + return indices; + j++; + } + return { -1, -1 }; +} + +bool render_vulkan_data::is_device_suitable(VkPhysicalDevice device, VkSurfaceKHR surface) { + std::pair indices = find_queue_families(device, surface); + + bool extensions_supported = check_device_extension_support(device); + + bool swapchain_adequate = false; + if (extensions_supported) { + swapchain_support_details swapchain_support = query_swapchain_support(device, surface); + swapchain_adequate = swapchain_support.formats.size() && swapchain_support.present_modes.size(); + } + + VkPhysicalDeviceFeatures supported_features; + vkGetPhysicalDeviceFeatures(device, &supported_features); + + return indices.first != -1 && indices.second != -1 && extensions_supported + && swapchain_adequate && supported_features.samplerAnisotropy; +} + +render_vulkan_data::swapchain_support_details render_vulkan_data::query_swapchain_support( + VkPhysicalDevice device, VkSurfaceKHR surface) { + swapchain_support_details details; + + vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &details.capabilities); + + uint32_t format_count; + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &format_count, 0); + + if (format_count) { + details.formats.resize(format_count); + vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &format_count, details.formats.data()); + } + + uint32_t present_mode_count; + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &present_mode_count, 0); + + if (present_mode_count) { + details.present_modes.resize(present_mode_count); + vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, + &present_mode_count, details.present_modes.data()); + } + + return details; +} + +#if RENDER_DEBUG +VKAPI_ATTR VkBool32 VKAPI_CALL render_vulkan_data::debug_callback( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, void* pUserData) { + if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) { + HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE); + + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(console, &csbi); + SetConsoleTextAttribute(console, FOREGROUND_INTENSITY | FOREGROUND_RED + | FOREGROUND_GREEN | FOREGROUND_BLUE | BACKGROUND_RED); + printf_debug("validation layer error: %s\n\n", pCallbackData->pMessage); + SetConsoleTextAttribute(console, csbi.wAttributes); + } + else + printf_debug("validation layer msg: %s\n", pCallbackData->pMessage); + return VK_FALSE; +} +#endif + +static bool render_init(render_init_struct* ris) { + GLFWmonitor* monitor = glfwGetPrimaryMonitor(); + const GLFWvidmode* mode = glfwGetVideoMode(monitor); + width = ris->res.x > 0 && ris->res.x < 8192 ? ris->res.x : mode->width; + height = ris->res.y > 0 && ris->res.y < 8192 ? ris->res.y : mode->height; + + vulkan_render = ris->vulkan_render; + + width = (int32_t)(width / 2.0f); + height = (int32_t)(height / 2.0f); + +#if BAKE_PNG || BAKE_VIDEO + width = 1920; + height = 1080; +#endif + if (vulkan_render) + glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + else + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); + glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE); + glfwWindowHint(GLFW_FOCUSED, GLFW_TRUE); + //glfwWindowHint(GLFW_TRANSPARENT_FRAMEBUFFER, GLFW_TRUE); +#if RENDER_DEBUG + if (!vulkan_render) + glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, true); +#endif + + glfwWindowHint(GLFW_RED_BITS, mode->redBits); + glfwWindowHint(GLFW_GREEN_BITS, mode->greenBits); + glfwWindowHint(GLFW_BLUE_BITS, mode->blueBits); + glfwWindowHint(GLFW_DEPTH_BITS, 0); + +#if BAKE_PNG || BAKE_VIDEO + bool maximized = false; +#else + bool maximized = mode->width == width && mode->height == height; +#endif + glfwWindowHint(GLFW_MAXIMIZED, maximized ? GLFW_TRUE : GLFW_FALSE); + + if (vulkan_render) + window = glfwCreateWindow(width, height, application_name, maximized ? monitor : 0, 0); + else { + int32_t minor = 6; + window = 0; + while (!window || minor < 3) { + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, minor--); + + window = glfwCreateWindow(width, height, application_name, maximized ? monitor : 0, 0); + } + } + + if (!window) + return false; + + if (vulkan_render) { + render_vulkan = new render_vulkan_data; + if (!render_vulkan->load()) { + glfwDestroyWindow(window); + return false; + } + } + else { + render_opengl = new render_opengl_data; + if (!render_opengl->load()) { + glfwDestroyWindow(window); + return false; + } + } + return true; +} + +static void render_main_loop(render_context* rctx) { + render_timer->reset(); + while (!close && !reload_render) { + render_timer->start_of_cycle(); + glfwPollEvents(); + + if (!vulkan_render) ImGui_ImplGlfw_NewFrame(); + if (vulkan_render); + //ImGui_ImplVulkan_NewFrame(); + else + ImGui_ImplOpenGL3_NewFrame(); + if (!vulkan_render) ImGui::NewFrame(); + + Input::NewFrame(); + + lock_lock(render_lock); + if (!vulkan_render) render_context_ctrl(rctx); + lock_unlock(render_lock); + render_context_disp(rctx); + + close |= !!glfwWindowShouldClose(window); + frame_counter++; + + Input::EndFrame(); + + if (!vulkan_render) + glfwSwapBuffers(window); + render_timer->end_of_cycle(); + } +} + +static void render_free() { + if (vulkan_render) { + render_vulkan->unload(); + delete render_vulkan; + } + else { + render_opengl->unload(); + delete render_opengl; + } + + glfwDestroyWindow(window); +} + +static render_context* render_context_load() { + data_struct_init(); + data_struct_load("ReDIVA_data.txt"); + data_struct_load_db(); + + if (vulkan_render) + render_vulkan_shaders_load(); + else + render_opengl_shaders_load(); + texture_storage_init(); file_handler_storage_init(); @@ -404,18 +1319,13 @@ static render_context* render_load() { render_context* rctx = new render_context; rctx_ptr = rctx; - gl_state_get(); - render_texture_data_init(); - - motion_storage_init(); - mothead_storage_init(); - osage_setting_data_init(); - - game_state_init(); - - data_struct_init(); - data_struct_load("ReDIVA_data.txt"); - data_struct_load_db(); + if (vulkan_render) + render_texture_vulkan_data_init(render_vulkan->device, + render_vulkan->allocator, render_vulkan->command_pool, render_vulkan->graphics_queue); + else { + gl_state_get(); + render_texture_opengl_data_init(); + } data_struct* aft_data = &data_list[DATA_AFT]; auth_3d_database* aft_auth_3d_db = &aft_data->data_ft.auth_3d_db; @@ -425,13 +1335,15 @@ static render_context* render_load() { texture_database* aft_tex_db = &aft_data->data_ft.tex_db; stage_database* aft_stage_data = &aft_data->data_ft.stage_data; - for (std::string& i : mdata_manager_get()->prefixes) { - std::string osage_setting_file = i + "osage_setting.txt"; - aft_data->load_file(aft_data, "rom/skin_param/", - osage_setting_file.c_str(), osage_setting_data_load_file); - } + if (!vulkan_render) { app::task_work_init(); + motion_init(); + mothead_storage_init(); + skin_param_data_init(); + + game_state_init(); + + skin_param_data_load(); - render_shaders_load(); sound_init(); wave_audio_storage_init(); ogg_file_handler_storage_init(); @@ -439,11 +1351,9 @@ static render_context* render_load() { object_storage_init(aft_obj_db); stage_param_data_storage_init(); pv_expression_file_storage_init(); - item_table_array_init(); + item_table_handler_array_init(); rand_state_array_init(); - app::task_work_init(); - rob_init(); task_wind_init(); auth_3d_data_init(); @@ -464,10 +1374,6 @@ static render_context* render_load() { Glitter::glt_particle_manager->bone_data = aft_bone_data; if (false) { - 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; - data_struct* x_data = &data_list[DATA_X]; data_struct* xhd_data = &data_list[DATA_XHD]; @@ -477,7 +1383,7 @@ static render_context* render_load() { for (int32_t i = 800; i <= 831; i++) { sprintf_s(buf, sizeof(buf), i == 815 ? "EFFPV%03d" : "ITMPV%03d", i); - object_set_info* effpv_set_info; + const object_set_info* effpv_set_info; if (aft_obj_db->get_object_set_info(buf, &effpv_set_info)) { obj_set_ids.push_back(effpv_set_info->id); obj_set_id_name.insert({ effpv_set_info->id, buf }); @@ -485,7 +1391,7 @@ static render_context* render_load() { sprintf_s(buf, sizeof(buf), "STGPV%03d", i); - object_set_info* stgpv_set_info; + const object_set_info* stgpv_set_info; if (aft_obj_db->get_object_set_info(buf, &stgpv_set_info)) { obj_set_ids.push_back(stgpv_set_info->id); obj_set_id_name.insert({ stgpv_set_info->id, buf }); @@ -493,14 +1399,14 @@ static render_context* render_load() { sprintf_s(buf, sizeof(buf), "STGPV%03dHRC", i); - object_set_info* stgpvhrc_set_info; + const object_set_info* stgpvhrc_set_info; if (aft_obj_db->get_object_set_info(buf, &stgpvhrc_set_info)) { obj_set_ids.push_back(stgpvhrc_set_info->id); obj_set_id_name.insert({ stgpvhrc_set_info->id, buf }); } for (uint32_t& i : obj_set_ids) { - object_set_info* set_info; + const object_set_info* set_info; if (!aft_obj_db->get_object_set_info(i, &set_info)) continue; @@ -516,7 +1422,7 @@ static render_context* render_load() { if (!tex_ff) continue; - prj::shared_ptr alloc(new alloc_data); + prj::shared_ptr alloc(new prj::stack_allocator); obj_set obj_set; obj_set.unpack_file(alloc, obj_ff->data, obj_ff->size, false); @@ -593,7 +1499,7 @@ static render_context* render_load() { if (!txi) continue; - prj::shared_ptr alloc(new alloc_data); + prj::shared_ptr alloc(new prj::stack_allocator); obj_set obj_set; obj_set.unpack_file(alloc, osd->data, osd->size, true); @@ -686,7 +1592,7 @@ static render_context* render_load() { if (!txi) continue; - prj::shared_ptr alloc(new alloc_data); + prj::shared_ptr alloc(new prj::stack_allocator); obj_set obj_set; obj_set.unpack_file(alloc, osd->data, osd->size, true); @@ -722,185 +1628,19 @@ static render_context* render_load() { } } - glGenBuffers(1, &common_data_ubo); - - gl_state_bind_uniform_buffer(common_data_ubo); - if (GLAD_GL_VERSION_4_4) - glBufferStorage(GL_UNIFORM_BUFFER, COMMON_DATA_SIZE, 0, GL_DYNAMIC_STORAGE_BIT); - else - glBufferData(GL_UNIFORM_BUFFER, COMMON_DATA_SIZE, 0, GL_STREAM_DRAW); - glBindBufferRange(GL_UNIFORM_BUFFER, 0, common_data_ubo, 0, COMMON_DATA_SIZE); - gl_state_bind_uniform_buffer(0); - - uniform_value[U16] = 1; - - const char* cube_line_vert_shader = - "#version 430 core\n" - "layout(location = 0) in vec4 a_position;\n" - "\n" - "out VertexData {\n" - " vec4 color;\n" - "} result;\n" - "\n" - "uniform mat4 vp;\n" - "uniform vec4 color;\n" - "\n" - "void main() {\n" - " gl_Position = vp * a_position;\n" - " result.color = color;\n" - "}\n"; - - const char* cube_line_frag_shader = - "#version 430 core\n" - "layout(location = 0) out vec4 result;\n" - "\n" - "in VertexData {\n" - " vec4 color;\n" - "} frg;\n" - "\n" - "void main() {\n" - " result = frg.color;\n" - "}\n"; - - const char* cube_line_point_vert_shader = - "#version 430 core\n" - "layout(location = 0) in vec3 i_trans;\n" - "\n" - "out VertexData {\n" - " vec2 uv;\n" - "} result;\n" - "\n" - "uniform mat4 vp;\n" - "uniform vec3 trans[4];\n" - "\n" - "void main() {\n" - " vec4 pos;\n" - " pos.xyz = trans[gl_VertexID] + i_trans;\n" - " pos.w = 1.0;\n" - " gl_Position = vp * pos;\n" - " vec2 uv;\n" - " uv.x = float(gl_VertexID / 2);\n" - " uv.y = float(gl_VertexID % 2);\n" - " result.uv = uv;\n" - "}\n"; - - const char* cube_line_point_frag_shader = - "#version 430 core\n" - "layout(location = 0) out vec4 result;\n" - "\n" - "in VertexData {\n" - " vec2 uv;\n" - "} frg;\n" - "\n" - "uniform float border_end;\n" - "uniform float border_start;\n" - "uniform vec3 border_color;" - "uniform vec3 center_color;" - "\n" - "void main() {\n" - " float blend = step(border_end, frg.uv.x) * (1.0 - step(border_start, frg.uv.x))" - " * step(border_end, frg.uv.y) * (1.0 - step(border_start, frg.uv.y));\n" - " result = vec4(mix(border_color, center_color, blend), 1.0);\n" - "}\n"; - - const char* grid_vert_shader = - "#version 430 core\n" - "layout(location = 0) in vec4 a_position;\n" - "layout(location = 1) in int a_color_index;\n" - "\n" - "out VertexData {\n" - " vec4 color;\n" - "} result;\n" - "\n" - "uniform mat4 vp;\n" - "\n" - "const vec4 colors[] = {\n" - " vec4(1.0, 0.0, 0.0, 1.0),\n" - " vec4(0.0, 1.0, 0.0, 1.0),\n" - " vec4(0.2, 0.2, 0.2, 1.0),\n" - "};\n" - "\n" - "void main() {\n" - " gl_Position = vp * a_position.xzyw;\n" - " result.color = colors[a_color_index];\n" - "}\n"; - - const char* grid_frag_shader = - "#version 430 core\n" - "layout(location = 0) out vec4 result;\n" - "\n" - "in VertexData {\n" - " vec4 color;\n" - "} frg;\n" - "\n" - "void main() {\n" - " result = frg.color;\n" - "}\n"; - - const char* fbo_render_vert_shader = - "#version 430 core\n" - "void main() {\n" - " gl_Position.x = -1.0 + float(gl_VertexID / 2) * 4.0;\n" - " gl_Position.y = 1.0 - float(gl_VertexID % 2) * 4.0;\n" - " gl_Position.z = 0.0;\n" - " gl_Position.w = 1.0;\n" - "}\n"; - - const char* fbo_render_color_frag_shader = - "#version 430 core\n" - "layout(location = 0) out vec4 result;\n" - "\n" - "layout(binding = 0) uniform sampler2D g_color;\n" - "\n" - "void main() {\n" - " result = texelFetch(g_color, ivec2(gl_FragCoord.xy), 0);\n" - "}\n"; - - const char* fbo_render_depth_frag_shader = - "#version 430 core\n" - "layout(location = 0) out vec4 result;\n" - "\n" - "layout(binding = 0) uniform sampler2D g_color;\n" - "layout(binding = 1) uniform sampler2D g_depth;\n" - "\n" - "void main() {\n" - " result = texelFetch(g_color, ivec2(gl_FragCoord.xy), 0);\n" - " gl_FragDepth = texelFetch(g_depth, ivec2(gl_FragCoord.xy), 0).r;\n" - "}\n"; - - cube_line_shader = new shader_glsl; - shader_glsl_param param = {}; - param.name = "Cube Line"; - cube_line_shader->load(cube_line_vert_shader, cube_line_frag_shader, 0, ¶m); - - cube_line_point_shader = new shader_glsl; - param = {}; - param.name = "Cube Line Point"; - cube_line_point_shader->load(cube_line_point_vert_shader, cube_line_point_frag_shader, 0, ¶m); - - grid_shader = new shader_glsl; - param = {}; - param.name = "Grid"; - grid_shader->load(grid_vert_shader, grid_frag_shader, 0, ¶m); - render_resize_fb(rctx, false); rctx->post_process.init_fbo(internal_3d_res.x, internal_3d_res.y, internal_2d_res.x, internal_2d_res.y, width, height); - rctx->draw_pass.resize(internal_2d_res.x, internal_2d_res.y); + rctx->render_manager.resize(internal_2d_res.x, internal_2d_res.y); render_resize_fb(rctx, true); - Glitter::glt_particle_manager_append_task(); - //app::TaskWork::AppendTask(&pv_game_data, "PVGAME", 0); + Glitter::glt_particle_manager_add_task(); + //app::TaskWork::AddTask(&pv_game_data, "PVGAME", 0); for (int32_t i = 0; i < ROB_CHARA_COUNT; i++) rob_chara_pv_data_array[i].type = ROB_CHARA_TYPE_NONE; - for (std::string& i : mdata_manager_get()->prefixes) { - std::string chritm_prop_file = i + "chritm_prop.farc"; - aft_data->load_file(aft_data, "rom/", chritm_prop_file.c_str(), item_table_array_load_file); - } - light_param_data_storage::load(aft_data); auth_3d_data_load_auth_3d_db(aft_auth_3d_db); @@ -920,87 +1660,6 @@ static render_context* render_load() { render_timer->end_of_cycle(); } - glGenVertexArrays(1, &cube_line_vao); - glGenBuffers(1, &cube_line_vbo); - gl_state_bind_vertex_array(cube_line_vao); - gl_state_bind_array_buffer(cube_line_vbo); - if (GLAD_GL_VERSION_4_4) - glBufferStorage(GL_ARRAY_BUFFER, sizeof(vec3) * 4, 0, 0); - else - glBufferData(GL_ARRAY_BUFFER, sizeof(vec3) * 4, 0, GL_STREAM_DRAW); - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(vec3), 0); - gl_state_bind_array_buffer(0); - - glGenVertexArrays(1, &cube_line_point_vao); - glGenBuffers(1, &cube_line_point_instance_vbo); - gl_state_bind_vertex_array(cube_line_point_vao); - gl_state_bind_array_buffer(cube_line_point_instance_vbo); - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(std::pair), 0); - glVertexAttribDivisor(0, 1); - gl_state_bind_array_buffer(0); - gl_state_bind_vertex_array(0); - - float_t* grid_verts = force_malloc_s(float_t, 3 * grid_vertex_count); - - size_t v = 0; - for (float_t x = -grid_size; x <= grid_size; x += grid_spacing) { - int32_t x_color_index; - int32_t z_color_index; - if (x == 0) { - x_color_index = 0; - z_color_index = 1; - } - else { - x_color_index = 2; - z_color_index = 2; - } - - grid_verts[v++] = x; - grid_verts[v++] = -grid_size; - *(int32_t*)&grid_verts[v++] = x_color_index; - - grid_verts[v++] = x; - grid_verts[v++] = grid_size; - *(int32_t*)&grid_verts[v++] = x_color_index; - - grid_verts[v++] = -grid_size; - grid_verts[v++] = x; - *(int32_t*)&grid_verts[v++] = z_color_index; - - grid_verts[v++] = grid_size; - grid_verts[v++] = x; - *(int32_t*)&grid_verts[v++] = z_color_index; - } - - glGenVertexArrays(1, &grid_vao); - gl_state_bind_vertex_array(grid_vao); - - glGenBuffers(1, &grid_vbo); - gl_state_bind_array_buffer(grid_vbo); - if (GLAD_GL_VERSION_4_4) - glBufferStorage(GL_ARRAY_BUFFER, sizeof(float_t) * 3 - * grid_vertex_count, grid_verts, 0); - else - glBufferData(GL_ARRAY_BUFFER, sizeof(float_t) * 3 - * grid_vertex_count, grid_verts, GL_STATIC_DRAW); - - glVertexAttrib4f(0, 0.0f, 0.0f, 0.0f, 1.0f); - glVertexAttribI1i(1, 2); - - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, - sizeof(float_t) * 3, (void*)0); // Pos - glEnableVertexAttribArray(1); - glVertexAttribIPointer(1, 1, GL_INT, - sizeof(float_t) * 3, (void*)(sizeof(float_t) * 2)); // Color - - gl_state_bind_array_buffer(0); - gl_state_bind_vertex_array(0); - - free(grid_verts); - camera* cam = rctx->camera; cam->initialize(aspect, internal_3d_res.x, internal_3d_res.y, @@ -1013,21 +1672,128 @@ static render_context* render_load() { cam->set_interest({ 0.0f, 1.0f, 0.0f }); //cam->set_fov(70.0); cam->set_view_point({ 0.0f, 1.4f, 1.0f }); - cam->set_interest({ 0.0f, 1.4f, 0.0f }); + cam->set_interest({ 0.0f, 1.4f, 0.0f }); } + + if (vulkan_render) + render_vulkan->load_common_data(); + else + render_opengl->load_common_data(); + + uniform_value[U16] = 1; + + if (vulkan_render) { +#pragma region Vulkan Pipeline Layout + VkPipelineLayoutCreateInfo triangle_pipeline_layout_create_info = {}; + triangle_pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; + + vkCreatePipelineLayout(render_vulkan->device, &triangle_pipeline_layout_create_info, + 0, &render_vulkan->triangle_pipeline_layout); +#pragma endregion + +#pragma region Vulkan Pipeline + shader_vulkan_pair shader_sun_no_textured = shaders_ft.get_vulkan_shader(SHADER_FT_SUN_NO_TEXTURED); + + render_vulkan_data::pipeline_builder_data& pipeline_builder = render_vulkan->pipeline_builder; + + VkPipelineShaderStageCreateInfo& shader_stage_vert = pipeline_builder.shader_stages[0]; + shader_stage_vert = {}; + shader_stage_vert.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shader_stage_vert.stage = VK_SHADER_STAGE_VERTEX_BIT; + shader_stage_vert.module = shader_sun_no_textured.first; + shader_stage_vert.pName = "main"; + + VkPipelineShaderStageCreateInfo& shader_stage_frag = pipeline_builder.shader_stages[1]; + shader_stage_frag = {}; + shader_stage_frag.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; + shader_stage_frag.stage = VK_SHADER_STAGE_FRAGMENT_BIT; + shader_stage_frag.module = shader_sun_no_textured.second; + shader_stage_frag.pName = "main"; + + VkPipelineVertexInputStateCreateInfo& vertex_input = pipeline_builder.vertex_input; + vertex_input = {}; + vertex_input.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; + render_texture_vulkan_data_get_vertex_input_binding_descriptions( + vertex_input.pVertexBindingDescriptions, + vertex_input.vertexBindingDescriptionCount); + render_texture_vulkan_data_get_vertex_input_attribute_descriptions( + vertex_input.pVertexAttributeDescriptions, + vertex_input.vertexAttributeDescriptionCount); + + VkPipelineInputAssemblyStateCreateInfo& input_assembly = pipeline_builder.input_assembly; + input_assembly = {}; + input_assembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; + input_assembly.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; + input_assembly.primitiveRestartEnable = VK_FALSE; + + VkPipelineRasterizationStateCreateInfo& rasterizer = pipeline_builder.rasterizer; + rasterizer = {}; + rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; + rasterizer.depthClampEnable = VK_FALSE; + rasterizer.rasterizerDiscardEnable = VK_FALSE; + rasterizer.polygonMode = VK_POLYGON_MODE_FILL; + rasterizer.lineWidth = 1.0f; + rasterizer.cullMode = VK_CULL_MODE_BACK_BIT; + rasterizer.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; + rasterizer.depthBiasEnable = VK_FALSE; + + VkPipelineColorBlendAttachmentState& color_blend_attachment = pipeline_builder.color_blend_attachment; + color_blend_attachment = {}; + color_blend_attachment.colorWriteMask = + VK_COLOR_COMPONENT_R_BIT + | VK_COLOR_COMPONENT_G_BIT + | VK_COLOR_COMPONENT_B_BIT + | VK_COLOR_COMPONENT_A_BIT; + color_blend_attachment.blendEnable = VK_TRUE; + color_blend_attachment.srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA; + color_blend_attachment.dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + color_blend_attachment.colorBlendOp = VK_BLEND_OP_ADD; + color_blend_attachment.srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE; + color_blend_attachment.dstAlphaBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA; + color_blend_attachment.alphaBlendOp = VK_BLEND_OP_ADD; + + VkPipelineMultisampleStateCreateInfo& multisampling = pipeline_builder.multisampling; + multisampling = {}; + multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; + multisampling.sampleShadingEnable = VK_FALSE; + multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; + + VkPipelineDepthStencilStateCreateInfo& depth_stencil = pipeline_builder.depth_stencil; + depth_stencil = {}; + depth_stencil.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; + + render_vulkan->pipeline_builder.build_pipeline(render_vulkan->device, + render_vulkan->render_pass, render_vulkan->triangle_pipeline); +#pragma endregion + } - imgui_context = ImGui::CreateContext(); imgui_context_lock = new lock_cs; - lock_lock(imgui_context_lock); + imgui_context = ImGui::CreateContext(); ImGui::SetCurrentContext(imgui_context); ImGuiIO& io = ImGui::GetIO(); io.IniFilename = 0; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; lock_unlock(imgui_context_lock); - ImGui::StyleColorsDark(0); - ImGui_ImplGlfw_InitForOpenGL(window, true); - ImGui_ImplOpenGL3_Init("#version 430"); + ImGui::StyleColorsDark(); + if (vulkan_render) { + /*ImGui_ImplVulkan_InitInfo init_info = {}; + init_info.Instance = render_vulkan->instance; + init_info.PhysicalDevice = render_vulkan->physical_device; + init_info.Device = render_vulkan->device; + init_info.Queue = render_vulkan->graphics_queue; + init_info.DescriptorPool = render_vulkan->descriptor_pool; + init_info.MinImageCount = 3; + init_info.ImageCount = 3; + init_info.MSAASamples = VK_SAMPLE_COUNT_1_BIT; + + ImGui_ImplGlfw_InitForVulkan(window, true); + ImGui_ImplVulkan_Init(&init_info, render_vulkan->render_pass);*/ + } + else { + ImGui_ImplGlfw_InitForOpenGL(window, true); + ImGui_ImplOpenGL3_Init("#version 430"); + } clear_color = { 0x60, 0x60, 0x60, 0xFF }; set_clear_color = true; @@ -1050,8 +1816,8 @@ static render_context* render_load() { modules[5] = 0;//31; //pv_game_data.Load(739, charas, modules); - shaders_ft.env_vert_set(3, 1.0f); - shaders_ft.env_vert_set(4, 0.0f); + rctx->obj_batch.g_blend_color = 1.0f; + rctx->obj_batch.g_offset_color = 0.0f; classes_process_init(classes, classes_count, rctx); return rctx; } @@ -1063,7 +1829,7 @@ extern double_t input_rotate_y; extern double_t input_roll; extern bool input_reset; -static void render_ctrl(render_context* rctx) { +static void render_context_ctrl(render_context* rctx) { camera* cam = rctx->camera; for (int32_t i = 0; i < 32; i++) { @@ -1080,72 +1846,6 @@ static void render_ctrl(render_context* rctx) { ImGui::SetCurrentContext(imgui_context); app::TaskWork_Window(); classes_process_imgui(classes, classes_count); - -#if DRAW_PASS_TIME_DISP - ImGui::SetNextWindowPos({ 0, 0 }, ImGuiCond_Appearing); - ImGui::SetNextWindowSize({ 1280, 636 }, ImGuiCond_Appearing); - - if (ImGui::Begin("CPU/GPU Time")) { - static const char* draw_pass_names[] = { - "Shadow", - "SS SSS", - "Pass 2", - "Reflect", - "Refract", - "Pre Process", - "Clear", - "Pre Sprite", - "3D", - "Show Vector", - "Post Process", - "Sprite", - "Pass 12", - }; - - char buf[0x200]; - for (int32_t i = 0; i < DRAW_PASS_MAX; i++) { - float_t cpu_min = FLT_MAX; - float_t cpu_max = 0.0f; - double_t cpu_avg = 0.0f; - float_t gpu_min = FLT_MAX; - float_t gpu_max = 0.0f; - double_t gpu_avg = 0.0f; - - for (float_t& j : draw_pass_cpu_time[i]) { - if (cpu_min > j) - cpu_min = j; - else if (cpu_max < j) - cpu_max = j; - cpu_avg += j; - } - - size_t cpu_time_count = draw_pass_cpu_time[i].size(); - if (cpu_time_count) - cpu_avg /= (double_t)cpu_time_count; - - for (float_t& j : draw_pass_gpu_time[i]) { - if (gpu_min > j) - gpu_min = j; - else if (gpu_max < j) - gpu_max = j; - gpu_avg += j; - } - - size_t gpu_time_count = draw_pass_gpu_time[i].size(); - if (gpu_time_count) - gpu_avg /= (double_t)gpu_time_count; - - sprintf_s(buf, sizeof(buf), "CPU Draw Pass % 12s; Min: % 3.4f; Max: % 3.4f; Avg: % 3.4lf", - draw_pass_names[i], cpu_min, cpu_max, cpu_avg); - ImGui::PlotLines(buf, draw_pass_cpu_time[i].data(), (int32_t)draw_pass_cpu_time[i].size()); - sprintf_s(buf, sizeof(buf), "GPU Draw Pass % 12s; Min: % 3.4f; Max: % 3.4f; Avg: % 3.4lf", - draw_pass_names[i], gpu_min, gpu_max, gpu_avg); - ImGui::PlotLines(buf, draw_pass_gpu_time[i].data(), (int32_t)draw_pass_gpu_time[i].size()); - } - ImGui::End(); - } -#endif - lock_unlock(imgui_context_lock); if (old_width != width || old_height != height || old_scale_index != scale_index) { @@ -1221,13 +1921,7 @@ static void render_ctrl(render_context* rctx) { ImGui::Render(); - struct common_data_struct { - vec4 res; //x=width, y=height, z=1/width, w=1/height - mat4 vp; - mat4 view; - mat4 projection; - vec3 view_pos; - } common_data; + common_data_struct common_data; common_data.res.x = (float_t)internal_3d_res.x; common_data.res.y = (float_t)internal_3d_res.y; @@ -1238,345 +1932,218 @@ static void render_ctrl(render_context* rctx) { common_data.projection = cam->projection; common_data.view_pos = cam->view_point; - gl_state_bind_uniform_buffer(common_data_ubo); - glBufferSubData(GL_UNIFORM_BUFFER, 0, COMMON_DATA_SIZE, &common_data); - gl_state_bind_uniform_buffer(0); - - cube_line_shader->set("vp", false, cam->view_projection); - cube_line_point_shader->set("vp", false, cam->view_projection); + if (vulkan_render) + render_vulkan->update_common_data(&common_data, cam); + else + render_opengl->update_common_data(&common_data, cam); } -static void cube_line_disp(shader_glsl* shader, camera* cam, vec3* trans, float_t line_size, vec4* color) { - mat4 mat[2]; - mat4_translate(&trans[0], &mat[0]); - mat4_translate(&trans[1], &mat[1]); - mat4_mult(&cam->view, &mat[0], &mat[0]); - mat4_mult(&cam->view, &mat[1], &mat[1]); +static void render_context_disp(render_context* rctx) { + //camera* cam = rctx->camera; - vec3 t[2]; - mat4_get_translation(&mat[0], &t[0]); - mat4_get_translation(&mat[1], &t[1]); + if (vulkan_render) { + render_vulkan->render_fence.WaitFor(); + render_vulkan->render_fence.Reset(); - vec3 d; - *(vec2*)&d = vec2::normalize(*(vec2*)&t[1] - *(vec2*)&t[0]) * line_size; - d.z = 0.0f; + uint32_t swapchain_image_index; + VkResult result = vkAcquireNextImageKHR(render_vulkan->device, render_vulkan->swapchain, + UINT64_MAX, render_vulkan->present_semaphore.data, 0, &swapchain_image_index); - vec3 norm[2]; - norm[0] = { -d.y, d.x, 0.0f }; - norm[1] = { d.y, -d.x, 0.0f }; - mat4_mult_vec3(&cam->inv_view_rot, &norm[0], &norm[0]); - mat4_mult_vec3(&cam->inv_view_rot, &norm[1], &norm[1]); - - vec3 vert_trans[4]; - vert_trans[0] = trans[0] + norm[0]; - vert_trans[1] = trans[0] + norm[1]; - vert_trans[2] = trans[1] + norm[0]; - vert_trans[3] = trans[1] + norm[1]; - - shader->set("color", *color); - - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vert_trans), vert_trans); - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); -} - -static int cube_line_points_sort(void const* src1, void const* src2) { - float_t d1 = ((std::pair*)src1)->second; - float_t d2 = ((std::pair*)src2)->second; - return d1 > d2 ? -1 : (d1 < d2 ? 1 : 0); -} - -static bool auth_3d_disp = false; -static bool rob_disp = false; - -static void render_disp(render_context* rctx) { - static const GLfloat color_clear[] = { 0.0f, 0.0f, 0.0f, 0.0f }; - static const GLfloat depth_clear = 1.0f; - static const GLint stencil_clear = 0; - - camera* cam = rctx->camera; - - for (int32_t i = 0; i < 32; i++) { - if (!gl_state_check_texture_binding_2d(i) && !gl_state_check_texture_binding_cube_map(i)) - continue; - - gl_state_active_bind_texture_2d(i, 0); - gl_state_active_bind_texture_cube_map(i, 0); - gl_state_bind_sampler(i, 0); - } - - gl_state_bind_framebuffer(0); - gl_state_set_depth_mask(GL_TRUE); - gl_state_set_stencil_mask(0xFF); - glClearBufferfv(GL_COLOR, 0, color_clear); - glClearDepthf(depth_clear); - gl_state_set_stencil_mask(0x00); - gl_state_set_depth_mask(GL_FALSE); - gl_state_bind_uniform_buffer_base(0, common_data_ubo); - - glViewport(0, 0, internal_3d_res.x, internal_3d_res.y); - - rctx->post_process.rend_texture.bind(); - gl_state_set_depth_mask(GL_TRUE); - glClearBufferfv(GL_COLOR, 0, color_clear); - glClearBufferfv(GL_DEPTH, 0, &depth_clear); - gl_state_set_depth_mask(GL_FALSE); - - cam->update(); - - rctx->disp(); - - int32_t screen_x_offset = (width - internal_2d_res.x) / 2 + (width - internal_2d_res.x) % 2; - int32_t screen_y_offset = (height - internal_2d_res.y) / 2 + (width - internal_2d_res.x) % 2; - glViewport(screen_x_offset, screen_y_offset, internal_2d_res.x, internal_2d_res.y); - rctx->post_process.screen_texture.bind(); - gl_state_bind_uniform_buffer_base(0, common_data_ubo); - classes_process_disp(classes, classes_count); - - gl_state_disable_cull_face(); - vec4 bone_color = { 1.0f, 0.0f, 0.0f, 1.0f }; - vec4 cns_color = { 1.0f, 1.0f, 0.0f, 1.0f }; - vec4 exp_color = { 0.0f, 1.0f, 0.0f, 1.0f }; - vec4 osg_color = { 0.0f, 0.0f, 1.0f, 1.0f }; - vec4 osg_node_color = { 1.0f, 0.0f, 1.0f, 1.0f }; - if (rob_disp) - for (int32_t i = 0; i < ROB_CHARA_COUNT; i++) { - if (rob_chara_pv_data_array[i].type == ROB_CHARA_TYPE_NONE) - continue; - - rob_chara* rob_chr = &rob_chara_array[i]; - if (!rob_chr->is_visible()) - continue; - - gl_state_bind_vertex_array(cube_line_vao); - gl_state_bind_array_buffer(cube_line_vbo); - rob_chara_bone_data* rob_bone_data = rob_chr->bone_data; - size_t object_bone_count = rob_bone_data->object_bone_count; - size_t total_bone_count = rob_bone_data->total_bone_count; - size_t ik_bone_count = rob_bone_data->ik_bone_count; - cube_line_shader->use(); - for (bone_node& j : rob_bone_data->nodes) { - if (!j.parent) - continue; - - vec3 trans[2]; - mat4_get_translation(j.parent->mat, &trans[0]); - mat4_get_translation(j.mat, &trans[1]); - - if (trans[0] != trans[1]) - cube_line_disp(cube_line_shader, cam, trans, CUBE_LINE_SIZE, &bone_color); - } - gl_state_bind_array_buffer(0); - - std::vector> cube_line_points; - for (bone_node& j : rob_bone_data->nodes) { - vec3 trans; - vec3 vp_trans; - mat4_get_translation(j.mat, &trans); - mat4_mult_vec3(&cam->view_projection, &trans, &vp_trans); - cube_line_points.push_back({ trans, vp_trans.z }); - } - - quicksort_custom(cube_line_points.data(), cube_line_points.size(), - sizeof(std::pair), cube_line_points_sort); - - gl_state_bind_array_buffer(cube_line_point_instance_vbo); - glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(sizeof(std::pair) - * cube_line_points.size()), cube_line_points.data(), GL_STREAM_DRAW); - gl_state_bind_array_buffer(0); - - vec3 trans[4]; - trans[0] = { -CUBE_LINE_POINT_SIZE, CUBE_LINE_POINT_SIZE, 0.0f }; - trans[1] = { CUBE_LINE_POINT_SIZE, CUBE_LINE_POINT_SIZE, 0.0f }; - trans[2] = { -CUBE_LINE_POINT_SIZE, -CUBE_LINE_POINT_SIZE, 0.0f }; - trans[3] = { CUBE_LINE_POINT_SIZE, -CUBE_LINE_POINT_SIZE, 0.0f }; - - mat4_mult_vec3(&cam->inv_view_rot, &trans[0], &trans[0]); - mat4_mult_vec3(&cam->inv_view_rot, &trans[1], &trans[1]); - mat4_mult_vec3(&cam->inv_view_rot, &trans[2], &trans[2]); - mat4_mult_vec3(&cam->inv_view_rot, &trans[3], &trans[3]); - - gl_state_bind_vertex_array(cube_line_point_vao); - cube_line_point_shader->use(); - cube_line_point_shader->set("trans", 4, trans); - cube_line_point_shader->set("border_end", - ((CUBE_LINE_POINT_SIZE - (CUBE_LINE_SIZE * 1.125f)) / (2.0f * CUBE_LINE_POINT_SIZE))); - cube_line_point_shader->set("border_start", - (1.0f - ((CUBE_LINE_POINT_SIZE - (CUBE_LINE_SIZE * 1.125f)) / (2.0f * CUBE_LINE_POINT_SIZE)))); - cube_line_point_shader->set("border_color", 0.0f, 0.0f, 0.0f); - cube_line_point_shader->set("center_color", 1.0f, 1.0f, 1.0f); - glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, (GLsizei)cube_line_points.size()); - gl_state_bind_vertex_array(0); - cube_line_points.clear(); - cube_line_points.shrink_to_fit(); + if (result == VK_ERROR_OUT_OF_DATE_KHR) { + //vulkanTestRecreateSwapChain(); + return; } + else if (result != VK_SUCCESS && result != VK_SUBOPTIMAL_KHR) + return; - if (auth_3d_disp) - for (auth_3d& i : auth_3d_data->data) { - if (i.id == -1 || !i.visible) - continue; + VkCommandBuffer cmd = render_vulkan->main_command_buffer.data; - for (auth_3d_object_hrc& j : i.object_hrc) { - if (!j.node[0].model_transform.visible) - continue; + vkResetCommandBuffer(cmd, 0); - gl_state_bind_vertex_array(cube_line_vao); - gl_state_bind_array_buffer(cube_line_vbo); - cube_line_shader->use(); - for (auth_3d_object_node& k : j.node) { - if (k.parent == -1 ) - continue; + VkCommandBufferBeginInfo command_buffer_begin_info = {}; + command_buffer_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + //command_buffer_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; - vec3 trans[2]; - mat4_get_translation(&j.node[k.parent].model_transform.mat, &trans[0]); - mat4_get_translation(&k.model_transform.mat, &trans[1]); + if (vkBeginCommandBuffer(cmd, &command_buffer_begin_info) != VK_SUCCESS) + return; - if (trans[0] != trans[1]) - cube_line_disp(cube_line_shader, cam, trans, CUBE_LINE_SIZE, &osg_color); - } - gl_state_bind_array_buffer(0); + VkClearValue clear_value; + float_t flash = (float_t)abs(sin(frame_counter / 120.0)); + clear_value.color = { { 0.0f, 0.0f, flash, 1.0f } }; - std::vector> cube_line_points; - for (auth_3d_object_node& k : j.node) { - vec3 trans; - vec3 vp_trans; - mat4_get_translation(&k.model_transform.mat, &trans); - mat4_mult_vec3(&cam->view_projection, &trans, &vp_trans); - cube_line_points.push_back({ trans, vp_trans.z }); - } + VkRenderPassBeginInfo render_pass_begin_info = {}; + render_pass_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; - quicksort_custom(cube_line_points.data(), cube_line_points.size(), - sizeof(std::pair), cube_line_points_sort); + render_pass_begin_info.renderPass = render_vulkan->render_pass; + render_pass_begin_info.renderArea.offset.x = 0; + render_pass_begin_info.renderArea.offset.y = 0; + render_pass_begin_info.renderArea.extent = render_vulkan->swapchain_extent; + render_pass_begin_info.framebuffer = render_vulkan->swapchain_framebuffers[swapchain_image_index].data; - gl_state_bind_array_buffer(cube_line_point_instance_vbo); - glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(sizeof(std::pair) - * cube_line_points.size()), cube_line_points.data(), GL_STREAM_DRAW); - gl_state_bind_array_buffer(0); + render_pass_begin_info.clearValueCount = 1; + render_pass_begin_info.pClearValues = &clear_value; - vec3 trans[4]; - trans[0] = { -CUBE_LINE_POINT_SIZE, CUBE_LINE_POINT_SIZE, 0.0f }; - trans[1] = { CUBE_LINE_POINT_SIZE, CUBE_LINE_POINT_SIZE, 0.0f }; - trans[2] = { -CUBE_LINE_POINT_SIZE, -CUBE_LINE_POINT_SIZE, 0.0f }; - trans[3] = { CUBE_LINE_POINT_SIZE, -CUBE_LINE_POINT_SIZE, 0.0f }; + vkCmdBeginRenderPass(cmd, &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE); - mat4_mult_vec3(&cam->inv_view_rot, &trans[0], &trans[0]); - mat4_mult_vec3(&cam->inv_view_rot, &trans[1], &trans[1]); - mat4_mult_vec3(&cam->inv_view_rot, &trans[2], &trans[2]); - mat4_mult_vec3(&cam->inv_view_rot, &trans[3], &trans[3]); + vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, render_vulkan->triangle_pipeline); - gl_state_bind_vertex_array(cube_line_point_vao); - cube_line_point_shader->use(); - cube_line_point_shader->set("trans", 4, trans); - cube_line_point_shader->set("border_end", - ((CUBE_LINE_POINT_SIZE - (CUBE_LINE_SIZE * 1.125f)) / (2.0f * CUBE_LINE_POINT_SIZE))); - cube_line_point_shader->set("border_start", - (1.0f - ((CUBE_LINE_POINT_SIZE - (CUBE_LINE_SIZE * 1.125f)) / (2.0f * CUBE_LINE_POINT_SIZE)))); - cube_line_point_shader->set("border_color", 0.0f, 0.0f, 0.0f); - cube_line_point_shader->set("center_color", 1.0f, 1.0f, 1.0f); - glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, (GLsizei)cube_line_points.size()); - gl_state_bind_vertex_array(0); - cube_line_points.clear(); - cube_line_points.shrink_to_fit(); - } - } + VkViewport viewport; + viewport.x = 0.0f; + viewport.y = 0.0f; + viewport.width = (float_t)render_vulkan->swapchain_extent.width; + viewport.height = (float_t)render_vulkan->swapchain_extent.height; + viewport.minDepth = 0.0f; + viewport.maxDepth = 1.0f; + vkCmdSetViewport(cmd, 0, 1, &viewport); - if (rob_disp) - for (int32_t i = 0; i < ROB_CHARA_COUNT; i++) { - if (rob_chara_pv_data_array[i].type == ROB_CHARA_TYPE_NONE) - continue; + VkRect2D scissor; + scissor.offset = { 0, 0 }; + scissor.extent = render_vulkan->swapchain_extent; + vkCmdSetScissor(cmd, 0, 1, &scissor); - rob_chara* rob_chr = &rob_chara_array[i]; - if (!rob_chr->is_visible()) - continue; + vkCmdDraw(cmd, 3, 1, 0, 0); - std::vector> cube_line_points; - motion_blend_mot* mot = rob_chr->bone_data->motion_loaded.front(); - mot_key_set* key_set = mot->mot_key_data.mot.key_sets; - for (bone_data& j : mot->bone_data.bones) - if (j.type >= BONE_DATABASE_BONE_HEAD_IK_ROTATION - && j.type <= BONE_DATABASE_BONE_LEGS_IK_ROTATION) { - vec3 trans; - mat4_mult_vec3(&cam->view_projection, &j.ik_target, &trans); - cube_line_points.push_back({ j.ik_target, trans.z }); - } + vkCmdEndRenderPass(cmd); + vkEndCommandBuffer(cmd); - quicksort_custom(cube_line_points.data(), cube_line_points.size(), - sizeof(std::pair), cube_line_points_sort); + VkSubmitInfo submit_info = {}; + submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submit_info.pNext = nullptr; - gl_state_bind_array_buffer(cube_line_point_instance_vbo); - glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)(sizeof(std::pair) - * cube_line_points.size()), cube_line_points.data(), GL_STREAM_DRAW); - gl_state_bind_array_buffer(0); + VkPipelineStageFlags wait_stage_flags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; - vec3 trans[4]; - trans[0] = { -CUBE_LINE_POINT_SIZE, CUBE_LINE_POINT_SIZE, 0.0f }; - trans[1] = { CUBE_LINE_POINT_SIZE, CUBE_LINE_POINT_SIZE, 0.0f }; - trans[2] = { -CUBE_LINE_POINT_SIZE, -CUBE_LINE_POINT_SIZE, 0.0f }; - trans[3] = { CUBE_LINE_POINT_SIZE, -CUBE_LINE_POINT_SIZE, 0.0f }; + submit_info.pWaitDstStageMask = &wait_stage_flags; - mat4_mult_vec3(&cam->inv_view_rot, &trans[0], &trans[0]); - mat4_mult_vec3(&cam->inv_view_rot, &trans[1], &trans[1]); - mat4_mult_vec3(&cam->inv_view_rot, &trans[2], &trans[2]); - mat4_mult_vec3(&cam->inv_view_rot, &trans[3], &trans[3]); + submit_info.waitSemaphoreCount = 1; + submit_info.pWaitSemaphores = &render_vulkan->present_semaphore.data; - gl_state_bind_vertex_array(cube_line_point_vao); - cube_line_point_shader->use(); - cube_line_point_shader->set("trans", 4, trans); - cube_line_point_shader->set("border_end", - ((CUBE_LINE_POINT_SIZE - (CUBE_LINE_SIZE * 1.125f)) / (2.0f * CUBE_LINE_POINT_SIZE))); - cube_line_point_shader->set("border_start", - (1.0f - ((CUBE_LINE_POINT_SIZE - (CUBE_LINE_SIZE * 1.125f)) / (2.0f * CUBE_LINE_POINT_SIZE)))); - cube_line_point_shader->set("border_color", 0.0f, 0.0f, 0.0f); - cube_line_point_shader->set("center_color", 0.0f, 1.0f, 0.0f); - glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, 4, (GLsizei)cube_line_points.size()); - gl_state_bind_vertex_array(0); - cube_line_points.clear(); - cube_line_points.shrink_to_fit(); - } + submit_info.signalSemaphoreCount = 1; + submit_info.pSignalSemaphores = &render_vulkan->render_semaphore.data; - gl_state_bind_vertex_array(0); - gl_state_enable_cull_face(); - gl_state_bind_framebuffer(0); + submit_info.commandBufferCount = 1; + submit_info.pCommandBuffers = &cmd; - if (rctx->draw_pass.enable[DRAW_PASS_POST_PROCESS]) - fbo::blit(rctx->post_process.screen_texture.fbos[0], 0, - 0, 0, width, height, - 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR); + if (vkQueueSubmit(render_vulkan->graphics_queue, 1, + &submit_info, render_vulkan->render_fence.data) == VK_SUCCESS) { + VkPresentInfoKHR present_info = {}; + present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; + present_info.pNext = nullptr; -#if DRAW_PASS_TIME_DISP - for (int32_t i = 0; i < DRAW_PASS_MAX; i++) { - float_t* cpu_time = draw_pass_cpu_time[i].data(); - size_t cpu_time_count = draw_pass_cpu_time[i].size(); - if (cpu_time_count) { - memmove(cpu_time, cpu_time + 1, sizeof(float_t) * (cpu_time_count - 1)); - cpu_time[cpu_time_count - 1] = (float_t)rctx->draw_pass.cpu_time[i]; - } + present_info.pSwapchains = &render_vulkan->swapchain; + present_info.swapchainCount = 1; - float_t* gpu_time = draw_pass_gpu_time[i].data(); - size_t gpu_time_count = draw_pass_gpu_time[i].size(); - if (gpu_time_count) { - memmove(gpu_time, gpu_time + 1, sizeof(float_t) * (gpu_time_count - 1)); - gpu_time[gpu_time_count - 1] = (float_t)rctx->draw_pass.gpu_time[i]; + present_info.pWaitSemaphores = &render_vulkan->render_semaphore.data; + present_info.waitSemaphoreCount = 1; + + present_info.pImageIndices = &swapchain_image_index; + + vkQueuePresentKHR(render_vulkan->graphics_queue, &present_info); } } -#endif + else { + camera* cam = rctx->camera; - if (draw_imgui) - render_imgui(rctx); + static const GLfloat color_clear[] = { 0.0f, 0.0f, 0.0f, 0.0f }; + static const GLfloat depth_clear = 1.0f; + static const GLint stencil_clear = 0; + + for (int32_t i = 0; i < 32; i++) { + if (!gl_state_check_texture_binding_2d(i) && !gl_state_check_texture_binding_cube_map(i)) + continue; + + gl_state_active_bind_texture_2d(i, 0); + gl_state_active_bind_texture_cube_map(i, 0); + gl_state_bind_sampler(i, 0); + } + + gl_state_bind_framebuffer(0); + gl_state_set_depth_mask(GL_TRUE); + gl_state_set_stencil_mask(0xFF); + glClearBufferfv(GL_COLOR, 0, color_clear); + glClearDepthf(depth_clear); + gl_state_set_stencil_mask(0x00); + gl_state_set_depth_mask(GL_FALSE); + + glViewport(0, 0, internal_3d_res.x, internal_3d_res.y); + + rctx->post_process.rend_texture.bind(); + gl_state_set_depth_mask(GL_TRUE); + glClearBufferfv(GL_COLOR, 0, color_clear); + glClearBufferfv(GL_DEPTH, 0, &depth_clear); + gl_state_set_depth_mask(GL_FALSE); + + cam->update(); + + rctx->disp(); + + int32_t screen_x_offset = (width - internal_2d_res.x) / 2 + (width - internal_2d_res.x) % 2; + int32_t screen_y_offset = (height - internal_2d_res.y) / 2 + (width - internal_2d_res.x) % 2; + glViewport(screen_x_offset, screen_y_offset, internal_2d_res.x, internal_2d_res.y); + rctx->post_process.screen_texture.bind(); + classes_process_disp(classes, classes_count); + gl_state_bind_framebuffer(0); + + if (rctx->render_manager.pass_sw[rndr::RND_PASSID_POSTPROCESS]) + fbo::blit(rctx->post_process.screen_texture.fbos[0], 0, + 0, 0, width, height, + 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR); + + if (draw_imgui) + render_context_imgui(rctx); + } } -static void render_dispose(render_context* rctx) { +static void render_context_imgui(render_context* rctx) { + lock_lock(imgui_context_lock); + ImGui::SetCurrentContext(imgui_context); + if (vulkan_render); + //ImGui_ImplVulkan_RenderDrawData(ImGui::GetDrawData(), render_vulkan->main_command_buffer); + else + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + lock_unlock(imgui_context_lock); +} + +static void render_context_dispose(render_context* rctx) { classes_process_dispose(classes, classes_count); - ImGui_ImplOpenGL3_Shutdown(); - ImGui_ImplGlfw_Shutdown(); + if (vulkan_render); + //ImGui_ImplVulkan_Shutdown(); + else + ImGui_ImplOpenGL3_Shutdown(); + if (!vulkan_render) ImGui_ImplGlfw_Shutdown(); + lock_lock(imgui_context_lock); ImGui::DestroyContext(imgui_context); + lock_unlock(imgui_context_lock); delete imgui_context_lock; imgui_context_lock = 0; - //pv_game_data.SetDest(); - Glitter::glt_particle_manager_free_task(); - task_auth_3d_free_task(); - task_pv_db_free_task(); + if (vulkan_render) { +#pragma region Vulkan Pipeline + if (render_vulkan->triangle_pipeline) { + vkDestroyPipeline(render_vulkan->device, + render_vulkan->triangle_pipeline, 0); + render_vulkan->triangle_pipeline = 0; + } +#pragma endregion + +#pragma region Vulkan Pipeline Layout + if (render_vulkan->triangle_pipeline_layout) { + vkDestroyPipelineLayout(render_vulkan->device, + render_vulkan->triangle_pipeline_layout, 0); + render_vulkan->triangle_pipeline_layout = 0; + } +#pragma endregion + } + + if (vulkan_render) + render_vulkan->unload_common_data(); + else + render_opengl->unload_common_data(); + + //pv_game_data.DelTask(); + if (!vulkan_render) { Glitter::glt_particle_manager_del_task(); + task_auth_3d_del_task(); + task_pv_db_del_task(); app::TaskWork::Dest(); @@ -1607,21 +2174,6 @@ static void render_dispose(render_context* rctx) { light_param_data_storage::unload(); - if (grid_shader) { - delete grid_shader; - grid_shader = 0; - } - - if (cube_line_point_shader) { - delete cube_line_point_shader; - cube_line_point_shader = 0; - } - - if (cube_line_shader) { - delete cube_line_shader; - cube_line_shader = 0; - } - task_data_test_glitter_particle_free(); dtw_stg_free(); auth_3d_test_window_free(); @@ -1639,18 +2191,8 @@ static void render_dispose(render_context* rctx) { task_wind_free(); rob_free(); - glDeleteBuffers(1, &common_data_ubo); - glDeleteBuffers(1, &grid_vbo); - glDeleteVertexArrays(1, &grid_vao); - glDeleteBuffers(1, &cube_line_point_instance_vbo); - glDeleteVertexArrays(1, &cube_line_point_vao); - glDeleteBuffers(1, &cube_line_vbo); - glDeleteVertexArrays(1, &cube_line_vao); - - app::task_work_free(); - rand_state_array_free(); - item_table_array_free(); + item_table_handler_array_free(); pv_expression_file_storage_free(); stage_param_data_storage_free(); object_storage_free(); @@ -1658,28 +2200,49 @@ static void render_dispose(render_context* rctx) { ogg_file_handler_storage_free(); wave_audio_storage_free(); sound_free(); - render_shaders_free(); - data_struct_free(); - - osage_setting_data_free(); + skin_param_data_free(); mothead_storage_free(); - motion_storage_free(); + motion_free(); - render_texture_data_free(); + app::task_work_free();} + + if (vulkan_render) + render_texture_vulkan_data_free(render_vulkan->device, render_vulkan->allocator); + else + render_texture_opengl_data_free(); + + delete rctx; file_handler_storage_free(); - delete rctx; + + texture_storage_free(); + + if (vulkan_render) + render_vulkan_shaders_free(); + else + render_opengl_shaders_free(); + + data_struct_free(); } -static void render_imgui(render_context* rctx) { - lock_lock(imgui_context_lock); - ImGui::SetCurrentContext(imgui_context); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - lock_unlock(imgui_context_lock); +static void render_opengl_shaders_load() { + data_list[DATA_AFT].load_file(&shaders_ft, "rom/", "ft_shaders.farc", render_opengl_load_shaders); } -static bool render_load_shaders(void* data, const char* path, const char* file, uint32_t hash) { +static void render_opengl_shaders_free() { + shaders_ft.unload_opengl(); +} + +static void render_vulkan_shaders_load() { + data_list[DATA_AFT].load_file(&shaders_ft, "rom/", "ft_shaders_spirv.farc", render_vulkan_load_shaders); +} + +static void render_vulkan_shaders_free() { + shaders_ft.unload_vulkan(); +} + +static bool render_opengl_load_shaders(void* data, const char* path, const char* file, uint32_t hash) { shader_set_data* set = (shader_set_data*)data; std::string s; s.assign(path); @@ -1687,9 +2250,24 @@ static bool render_load_shaders(void* data, const char* path, const char* file, farc f; f.read(s.c_str(), true, false); - set->load(&f, false, "ft", shader_ft_table, shader_ft_table_size, - shader_ft_bind_func_table, shader_ft_bind_func_table_size); + set->load_opengl(&f, false, "ft", shader_ft_table, shader_ft_table_size, + shader_ft_opengl_bind_func_table, shader_ft_opengl_bind_func_table_size, + shader_ft_get_index_by_name); + return true; +} +static bool render_vulkan_load_shaders(void* data, const char* path, const char* file, uint32_t hash) { + shader_set_data* set = (shader_set_data*)data; + std::string s; + s.assign(path); + s.append(file); + + farc f; + f.read(s.c_str(), true, false); + set->load_vulkan(render_vulkan->device, render_vulkan->allocator, + &f, shader_ft_table, shader_ft_table_size, + shader_ft_vulkan_get_func_table, shader_ft_vulkan_get_func_table_size, + shader_ft_get_index_by_name); return true; } @@ -1709,8 +2287,10 @@ static void render_resize_fb_glfw(GLFWwindow* window, int32_t w, int32_t h) { } static void render_resize_fb(render_context* rctx, bool change_fb) { - if (internal_3d_res.x < 20) internal_3d_res.x = 20; - if (internal_3d_res.y < 20) internal_3d_res.y = 20; + if (internal_3d_res.x < 20) + internal_3d_res.x = 20; + if (internal_3d_res.y < 20) + internal_3d_res.y = 20; double_t res_width = (double_t)width; double_t res_height = (double_t)height; @@ -1753,7 +2333,7 @@ static void render_resize_fb(render_context* rctx, bool change_fb) { if (fb_changed && change_fb) { rctx->post_process.init_fbo(internal_3d_res.x, internal_3d_res.y, internal_2d_res.x, internal_2d_res.y, width, height); - rctx->draw_pass.resize(internal_2d_res.x, internal_2d_res.y); + rctx->render_manager.resize(internal_2d_res.x, internal_2d_res.y); rctx->litproj->resize(internal_3d_res.x, internal_3d_res.y); } } @@ -1798,15 +2378,7 @@ static void render_imgui_context_menu(classes_data* classes, } } -static void render_shaders_load() { - data_list[DATA_AFT].load_file(&shaders_ft, "rom/", "ft_shaders.farc", render_load_shaders); -} - -static void render_shaders_free() { - shaders_ft.unload(); -} - -#if defined(DEBUG) && OPENGL_DEBUG +#if RENDER_DEBUG static void APIENTRY render_debug_output(GLenum source, GLenum type, uint32_t id, GLenum severity, GLsizei length, const char* message, const void* userParam) { if (id == 131169 || id == 131185 || id == 131218 || id == 131204) @@ -1882,4 +2454,26 @@ static void APIENTRY render_debug_output(GLenum source, GLenum type, uint32_t id printf_debug("Debug message (%d): %s\n", id, message); printf_debug("########################################\n\n"); } + +static VkResult vkGetInstanceProcAddrCreateDebugUtilsMessengerEXT(VkInstance instance, + const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo, + const VkAllocationCallbacks* pAllocator, VkDebugUtilsMessengerEXT* pDebugMessenger) { + static PFN_vkCreateDebugUtilsMessengerEXT _vkCreateDebugUtilsMessengerEXT; + if (!_vkCreateDebugUtilsMessengerEXT) + _vkCreateDebugUtilsMessengerEXT = (PFN_vkCreateDebugUtilsMessengerEXT) + vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"); + if (_vkCreateDebugUtilsMessengerEXT) + return _vkCreateDebugUtilsMessengerEXT(instance, pCreateInfo, pAllocator, pDebugMessenger); + return VK_ERROR_EXTENSION_NOT_PRESENT; +} + +static void vkGetInstanceProcAddrDestroyDebugUtilsMessengerEXT(VkInstance instance, + VkDebugUtilsMessengerEXT messenger, const VkAllocationCallbacks* pAllocator) { + static PFN_vkDestroyDebugUtilsMessengerEXT _vkDestroyDebugUtilsMessengerEXT; + if (!_vkDestroyDebugUtilsMessengerEXT) + _vkDestroyDebugUtilsMessengerEXT = (PFN_vkDestroyDebugUtilsMessengerEXT) + vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"); + if (_vkDestroyDebugUtilsMessengerEXT) + _vkDestroyDebugUtilsMessengerEXT(instance, messenger, pAllocator); +} #endif diff --git a/src/ReDIVA/render.hpp b/src/ReDIVA/render.hpp index 684532fe..e0a1e6ae 100644 --- a/src/ReDIVA/render.hpp +++ b/src/ReDIVA/render.hpp @@ -30,8 +30,11 @@ enum render_scale { struct render_init_struct { vec2i res; int32_t scale_index; + bool vulkan_render; - render_init_struct(); + inline render_init_struct() : scale_index(), vulkan_render() { + + } }; extern const double_t render_scale_table[]; diff --git a/src/ReDIVA/system_startup.cpp b/src/ReDIVA/system_startup.cpp index 40a95600..e5763fdc 100644 --- a/src/ReDIVA/system_startup.cpp +++ b/src/ReDIVA/system_startup.cpp @@ -51,7 +51,7 @@ namespace system_startup_detail { system_startup.state = 2; break; case 2: - mdata_manager_append_task(); + mdata_manager_add_task(); system_startup.state = 3; break; case 3: @@ -59,7 +59,7 @@ namespace system_startup_detail { system_startup.state = 4; break; case 4: - rctx_ptr->draw_pass.set_enable(DRAW_PASS_3D, false); + rctx_ptr->render_manager.set_pass_sw(rndr::RND_PASSID_ALL_3D, false); task_pv_game_init_test_pv(); system_startup.state = 5; break; @@ -76,7 +76,7 @@ namespace system_startup_detail { system_startup.state = 8; break; case 8: - rctx_ptr->draw_pass.set_enable(DRAW_PASS_3D, true); + rctx_ptr->render_manager.set_pass_sw(rndr::RND_PASSID_ALL_3D, true); system_startup_ready = 1; break; } @@ -87,26 +87,26 @@ namespace system_startup_detail { if (task_pv_game_check_task_ready() && !system_startup_check_ready()) return false; - opd_make_manager_task_free(); - mdata_manager_free_task(); + opd_make_manager_del_task(); + mdata_manager_del_task(); return true; } } -bool task_system_startup_append_task() { - return app::TaskWork::AppendTask(&task_system_startup, "SYSTEM_STARTUP"); +bool task_system_startup_add_task() { + return app::TaskWork::AddTask(&task_system_startup, "SYSTEM_STARTUP"); } -bool task_system_startup_free_task() { - return task_system_startup.SetDest(); +bool task_system_startup_del_task() { + return task_system_startup.DelTask(); } static bool system_startup_check_ready() { if (test_mode_get() || system_startup.ready) return true; - task_pv_game_free_task(); - if (!task_pv_game_check_task_ready() && task_rob_manager_free_task()) + task_pv_game_del_task(); + if (!task_pv_game_check_task_ready() && task_rob_manager_del_task()) system_startup.ready = true; return system_startup.ready; } @@ -115,7 +115,7 @@ static void task_pv_game_init_test_pv() { if (test_mode_get()) return; - task_rob_manager_append_task(); + task_rob_manager_add_task(); TaskPvGame::InitData init_data; init_data.data.pv_id = 999; init_data.data.difficulty = 1; @@ -130,5 +130,5 @@ static void task_pv_game_init_test_pv() { init_data.field_194 = true; init_data.field_195 = true; init_data.field_198 = true; - task_pv_game_append_task(&init_data); + task_pv_game_add_task(&init_data); } diff --git a/src/ReDIVA/system_startup.hpp b/src/ReDIVA/system_startup.hpp index b62c99c0..48ef3a44 100644 --- a/src/ReDIVA/system_startup.hpp +++ b/src/ReDIVA/system_startup.hpp @@ -24,5 +24,5 @@ namespace system_startup_detail { extern int32_t system_startup_ready; -extern bool task_system_startup_append_task(); -extern bool task_system_startup_free_task(); +extern bool task_system_startup_add_task(); +extern bool task_system_startup_del_task(); diff --git a/src/ReDIVA/task_window.cpp b/src/ReDIVA/task_window.cpp index 2671c432..fc93563d 100644 --- a/src/ReDIVA/task_window.cpp +++ b/src/ReDIVA/task_window.cpp @@ -46,8 +46,8 @@ namespace app { } static void TaskWindow_do_disp(TaskWindow* t) { - if ((t->field_1C == 1 || t->field_1C == 2) - && t->field_18 != Task::Enum::Init && t->field_18 != Task::Enum::Dest) { + if ((t->state == Task::State::Running || t->state == Task::State::Suspended) + && t->op != Task::Op::Init && t->op != Task::Op::Dest) { t->Window(); t->first_show = false; } diff --git a/src/ReDIVA/x_pv_game.cpp b/src/ReDIVA/x_pv_game.cpp index c3619c4d..fa886bae 100644 --- a/src/ReDIVA/x_pv_game.cpp +++ b/src/ReDIVA/x_pv_game.cpp @@ -1,14 +1,15 @@ /* by korenkonder GitHub/GitLab: korenkonder - - Some code is from LearnOpenGL */ #if defined(ReDIVA_DEV) #include "x_pv_game.hpp" #include "../CRE/light_param/light.hpp" #include "../CRE/light_param.hpp" +#include "../CRE/rob/rob.hpp" +#include "../CRE/rob/motion.hpp" +#include "../CRE/rob/skin_param.hpp" #include "../CRE/data.hpp" #include "../CRE/item_table.hpp" #include "../CRE/object.hpp" @@ -2715,7 +2716,8 @@ void x_pv_game_stage::set_change_effect_frame_part_2(float_t frame) { if (i.name.hash_murmurhash == hash_murmurhash_empty) continue; - i.scene_counter = Glitter::glt_particle_manager->LoadSceneEffect(i.name.hash_murmurhash, i.name.c_str()); + i.scene_counter = Glitter::glt_particle_manager->LoadSceneEffect( + i.name.hash_murmurhash, i.name.c_str(), 0x01); if (!i.scene_counter) continue; @@ -2798,7 +2800,8 @@ void x_pv_game_stage::set_stage_effect_glitter_frame(int32_t stage_effect, float std::vector& glitter = effect[stage_effect - 1ULL].glitter; for (x_pv_game_stage_effect_glitter& i : glitter) { - i.scene_counter = Glitter::glt_particle_manager->LoadSceneEffect(i.name.hash_murmurhash, i.name.c_str()); + i.scene_counter = Glitter::glt_particle_manager->LoadSceneEffect( + i.name.hash_murmurhash, i.name.c_str(), 0x01); if (!i.scene_counter) continue; @@ -2982,7 +2985,7 @@ x_pv_game::~x_pv_game() { } bool x_pv_game::Init() { - task_rob_manager_append_task(); + task_rob_manager_add_task(); return true; } @@ -3207,7 +3210,7 @@ bool x_pv_game::Ctrl() { x_pv_game_dsc_data& dsc_data = pv_data[pv_index].dsc_data; - app::TaskWork::AppendTask(&pv_param_task::post_process_task, "PV POST PROCESS TASK"); + app::TaskWork::AddTask(&pv_param_task::post_process_task, "PV POST PROCESS TASK"); { data_struct* x_data = &data_list[DATA_X]; @@ -3777,7 +3780,7 @@ bool x_pv_game::Ctrl() { break; Glitter::glt_particle_manager->FreeScenes(); - SetDest(); + DelTask(); } break; } return false; @@ -3787,15 +3790,15 @@ bool x_pv_game::Dest() { if (!Unload()) return false; - task_rob_manager_free_task(); + task_rob_manager_del_task(); light_param_data_storage_data_reset(); rctx_ptr->post_process.tone_map->set_saturate_coeff(1.0f); rctx_ptr->post_process.tone_map->set_scene_fade(0.0f); rctx_ptr->post_process.tone_map->set_scene_fade_blend_func(0); rctx_ptr->post_process.dof->data.pv.enable = false; - rctx_ptr->object_data.object_culling = true; - rctx_ptr->draw_pass.shadow_ptr->range = 1.0f; + rctx_ptr->disp_manager.object_culling = true; + rctx_ptr->render_manager.shadow_ptr->range = 1.0f; Glitter::glt_particle_manager->SetPause(false); extern float_t frame_speed; @@ -3875,44 +3878,41 @@ void x_pv_game::Basic() { frame_speed = pause ? 0.0f : 1.0f; #if BAKE_PV826 - if (get_delta_frame() != 0.0f && frame > 0) - for (auto& i : effchrpv_auth_3d_rob_mot_ids) { - rob_chara* rob_chr = rob_chara_array_get(rob_chara_ids[i.first]); - x_pv_game_a3da_to_mot& a2m = i.second; + for (auto& i : effchrpv_auth_3d_rob_mot_ids) { + rob_chara* rob_chr = rob_chara_array_get(rob_chara_ids[i.first]); + x_pv_game_a3da_to_mot& a2m = i.second; - rob_chara_bone_data* rob_bone_data = rob_chr->bone_data; + rob_chara_bone_data* rob_bone_data = rob_chr->bone_data; - bone_database_skeleton_type skeleton_type = rob_bone_data->base_skeleton_type; - motion_blend_mot* mot = rob_bone_data->motion_loaded.front(); - std::vector* bones = &mot->bone_data.bones; - std::vector* bone_indices = &mot->bone_data.bone_indices; + bone_database_skeleton_type skeleton_type = rob_bone_data->base_skeleton_type; + motion_blend_mot* mot = rob_bone_data->motion_loaded.front(); + std::vector* bones = &mot->bone_data.bones; + std::vector* bone_indices = &mot->bone_data.bone_indices; - bone_data* bones_data = bones->data(); - for (uint16_t& i : *bone_indices) { - bone_data* data = &bones_data[i]; - ::motion_bone_index motion_bone_index = (::motion_bone_index)data->motion_bone_index; - if (motion_bone_index >= MOTION_BONE_N_HITO_L_EX - && motion_bone_index <= MOTION_BONE_NL_OYA_C_L_WJ) { - auto elem = a2m.bone_keys.find(motion_bone_index); - if (elem == a2m.bone_keys.end()) - elem = a2m.bone_keys.insert({ motion_bone_index, {} }).first; + bone_data* bones_data = bones->data(); + for (uint16_t& i : *bone_indices) { + bone_data* data = &bones_data[i]; + ::motion_bone_index motion_bone_index = (::motion_bone_index)data->motion_bone_index; + if (!(motion_bone_index >= MOTION_BONE_N_HITO_L_EX + && motion_bone_index <= MOTION_BONE_NL_OYA_C_L_WJ + || motion_bone_index >= MOTION_BONE_N_HITO_R_EX + && motion_bone_index <= MOTION_BONE_NL_OYA_C_R_WJ)) + continue; - elem->second.x.push_back(data->rotation.x); - elem->second.y.push_back(data->rotation.y); - elem->second.z.push_back(data->rotation.z); - } - else if (motion_bone_index >= MOTION_BONE_N_HITO_R_EX - && motion_bone_index <= MOTION_BONE_NL_OYA_C_R_WJ) { - auto elem = a2m.bone_keys.find(motion_bone_index); - if (elem == a2m.bone_keys.end()) - elem = a2m.bone_keys.insert({ motion_bone_index, {} }).first; + auto elem = a2m.bone_keys.find(motion_bone_index); + if (elem == a2m.bone_keys.end()) + elem = a2m.bone_keys.insert({ motion_bone_index, {} }).first; - elem->second.x.push_back(data->rotation.x); - elem->second.y.push_back(data->rotation.y); - elem->second.z.push_back(data->rotation.z); - } - } + vec3 rotation; + mat4_get_rotation(&data->rot_mat[0], &rotation); + if (elem->second.x.size() == frame) + elem->second.x.push_back(rotation.x); + if (elem->second.y.size() == frame) + elem->second.y.push_back(rotation.y); + if (elem->second.z.size() == frame) + elem->second.z.push_back(rotation.z); } + } #endif #if BAKE_VIDEO @@ -4254,13 +4254,13 @@ bool mot_write_motion(void* data, const char* path, const char* file, uint32_t h char buf[0x200]; sprintf_s(buf, sizeof(buf), "PV%03d", xpvgm->pv_id); - motion_set_info* set_info = aft_mot_db->get_motion_set_by_name(buf); + const motion_set_info* set_info = aft_mot_db->get_motion_set_by_name(buf); if (!set_info) return true; std::string mot_file = "mot_" + set_info->name + ".bin"; - prj::shared_ptr alloc = prj::shared_ptr(new alloc_data); + prj::shared_ptr alloc = prj::shared_ptr(new prj::stack_allocator); ::mot_set* mot_set = alloc->allocate<::mot_set>(); { @@ -4288,7 +4288,7 @@ bool mot_write_motion(void* data, const char* path, const char* file, uint32_t h int32_t motion_id = aft_mot_db->get_motion_id(buf); size_t motion_index = -1; - for (motion_info& j : set_info->motion) + for (const motion_info& j : set_info->motion) if (j.id == motion_id) { motion_index = &j - set_info->motion.data(); break; @@ -4302,7 +4302,7 @@ bool mot_write_motion(void* data, const char* path, const char* file, uint32_t h const char* name = bone_database_skeleton_type_to_string(BONE_DATABASE_SKELETON_COMMON); std::string* bone_names = aft_mot_db->bone_name.data(); - std::vector* bones = 0; + const std::vector* bones = 0; if (!aft_bone_data->get_skeleton_bones(name, &bones)) continue; @@ -4319,7 +4319,7 @@ bool mot_write_motion(void* data, const char* path, const char* file, uint32_t h break; } - bone_database_bone* bone = &(*bones)[bone_index]; + const bone_database_bone* bone = &(*bones)[bone_index]; auto elem = a2m.bone_keys.find(bone_index); if (elem != a2m.bone_keys.end()) { @@ -4471,45 +4471,12 @@ bool x_pv_game::Unload() { #if BAKE_PV826 if (pv_data[pv_index].pv_id == 826) { - for (auto& i : effchrpv_auth_3d_rob_mot_ids) { - rob_chara* rob_chr = rob_chara_array_get(rob_chara_ids[i.first]); - x_pv_game_a3da_to_mot& a2m = i.second; - - bone_node* node = rob_chr->bone_data->nodes.data(); - - for (int32_t j = MOTION_BONE_N_HITO_L_EX; j <= MOTION_BONE_NL_OYA_C_L_WJ; j++) { - ::motion_bone_index motion_bone_index = (::motion_bone_index)j; - - auto elem = a2m.bone_keys.find(motion_bone_index); - if (elem == a2m.bone_keys.end()) - elem = a2m.bone_keys.insert({ motion_bone_index, {} }).first; - - vec3& rotation = node[j - MOTION_BONE_N_HITO_L_EX + ROB_BONE_N_HITO_L_EX].exp_data.rotation; - elem->second.x.push_back(rotation.x); - elem->second.y.push_back(rotation.y); - elem->second.z.push_back(rotation.z); - } - - for (int32_t j = MOTION_BONE_N_HITO_R_EX; j <= MOTION_BONE_NL_OYA_C_R_WJ; j++) { - ::motion_bone_index motion_bone_index = (::motion_bone_index)j; - - auto elem = a2m.bone_keys.find(motion_bone_index); - if (elem == a2m.bone_keys.end()) - elem = a2m.bone_keys.insert({ motion_bone_index, {} }).first; - - vec3& rotation = node[j - MOTION_BONE_N_HITO_R_EX + ROB_BONE_N_HITO_R_EX].exp_data.rotation; - elem->second.x.push_back(rotation.x); - elem->second.y.push_back(rotation.y); - elem->second.z.push_back(rotation.z); - } - } - data_struct* aft_data = &data_list[DATA_AFT]; motion_database* aft_mot_db = &aft_data->data_ft.mot_db; char buf[0x200]; sprintf_s(buf, sizeof(buf), "PV%03d", pv_data[pv_index].pv_id); - motion_set_info* set_info = aft_mot_db->get_motion_set_by_name(buf); + const motion_set_info* set_info = aft_mot_db->get_motion_set_by_name(buf); if (set_info) { std::string farc_file = "mot_" + set_info->name + ".farc"; aft_data->load_file(this, "rom/rob/", farc_file.c_str(), mot_write_motion); @@ -4517,6 +4484,9 @@ bool x_pv_game::Unload() { } #endif + for (int32_t& i : rob_chara_ids) + skin_param_manager_reset(i); + data_struct* aft_data = &data_list[DATA_AFT]; motion_database* aft_mot_db = &aft_data->data_ft.mot_db; @@ -4584,7 +4554,7 @@ bool x_pv_game::Unload() { i = 0; Glitter::glt_particle_manager->draw_all = true; - pv_param_task::post_process_task.SetDest(); + pv_param_task::post_process_task.DelTask(); return true; } @@ -4656,7 +4626,7 @@ void x_pv_game::ctrl(float_t curr_time, float_t delta_time) { if (!pv_data->state) pv_data->state = 20; else if (pv_data->state == 10) - field_1C |= 0x08; + pv_data->field_1C |= 0x08; } state = 20; } @@ -4691,8 +4661,8 @@ void x_pv_game::unload() { } XPVGameSelector::XPVGameSelector() : charas(), modules(), start(), exit() { - pv_id = 0; - stage_id = 0; + pv_id = 823; + stage_id = 23; for (chara_index& i : charas) i = CHARA_MIKU; @@ -4863,7 +4833,7 @@ void XPVGameSelector::Window() { } for (int32_t i = 0; i < ROB_CHARA_COUNT; i++) { - item_table* itm_tbl = item_table_array_get_table(charas[i]); + const item_table* itm_tbl = item_table_handler_array_get_table(charas[i]); sprintf_s(buf, sizeof(buf), "Module %dP", i + 1); sprintf_s(buf1, sizeof(buf1), "%d", modules[i]); @@ -4876,7 +4846,7 @@ void XPVGameSelector::Window() { ImGui::PushID(&j); sprintf_s(buf1, sizeof(buf1), "%d", j.first); if (ImGui::Selectable(buf1, modules[i] == j.first) - || ImGui::ItemKeyPressed(GLFW_KEY_ENTER, true) + || ImGui::ItemKeyPressed(ImGuiKey_Enter) || (ImGui::IsItemFocused() && modules[i] != j.first)) modules[i] = j.first; ImGui::PopID(); @@ -5910,7 +5880,7 @@ static bool x_pv_game_dsc_process(x_pv_game* a1, int64_t curr_time) { if (!rob_chr) break; - rob_chr->set_osage_step(osage_step_outer); + rob_chr->set_step(osage_step_outer); } break; case DSC_X_OSAGE_MV_CCL: { data_struct* aft_data = &data_list[DATA_AFT]; @@ -6125,7 +6095,7 @@ static bool x_pv_game_dsc_process(x_pv_game* a1, int64_t curr_time) { int32_t index = (int32_t)data[1]; int32_t unk2 = (int32_t)data[2]; - if (unk2 && !(a1->field_1C & 0x10)) + if (unk2 && !(a1->pv_data->field_1C & 0x10)) break; if (enable) { @@ -6829,7 +6799,7 @@ static void x_pv_game_split_auth_3d_hrc_material_list(x_pv_game* xpvgm, sizeof(x_pv_game_split_auth_3d_hrc_obj_bone), x_pv_game_split_auth_3d_hrc_obj_bone_compare_func); } - prj::shared_ptr& alloc = handler->alloc_handler; + prj::shared_ptr& alloc = handler->alloc_handler; uint32_t obj_num_new = (uint32_t)(obj_num + bones_vertices.size()); obj* obj_data_new = alloc->allocate<::obj>(obj_num_new); @@ -6990,9 +6960,9 @@ static void x_pv_game_split_auth_3d_hrc_material_list(x_pv_game* xpvgm, dst_obj->hash = hash; object_set_info* set_info; - if (obj_db.get_object_set_info(i.first.set_id, &set_info)) { + if (obj_db.get_object_set_info(i.first.set_id, (const object_set_info**)&set_info)) { object_info_data* info; - if (!obj_db.get_object_info_data_by_murmurhash(hash, &info)) { + if (!obj_db.get_object_info_data_by_murmurhash(hash, (const object_info_data**)&info)) { set_info->object.push_back({}); info = &set_info->object.back(); info->id = hash; @@ -7427,6 +7397,27 @@ static void pv_game_dsc_data_find_set_motion(x_pv_game* xpvgm) { set_motion.push_back({ motion_id, { v61, -1 } }); } } + + for (int32_t i = 0; i < ROB_CHARA_COUNT; i++) { + if (i < 0 || i >= play_param->chara.size()) + continue; + + rob_chara* rob_chr = rob_chara_array_get(i); + int32_t pv_id = xpvgm->pv_data[xpvgm->pv_index].pv_id; + + std::vector& set_motion = xpvgm->set_motion[i]; + std::vector vec; + vec.reserve(set_motion.size()); + for (pv_data_set_motion& j : set_motion) { + osage_init_data osage_init; + osage_init.rob_chr = rob_chr; + osage_init.pv_id = pv_id; + osage_init.motion_id = j.motion_id; + osage_init.frame = (int32_t)j.frame_stage_index.first; + vec.push_back(osage_init); + } + skin_param_manager_add_task(i, vec); + } } static void pv_game_dsc_data_set_motion_max_frame(x_pv_game* xpvgm, diff --git a/thirdparty.txt b/thirdparty.txt index cba936de..098558dc 100644 --- a/thirdparty.txt +++ b/thirdparty.txt @@ -57,7 +57,7 @@ The glad source code: The MIT License (MIT) - Copyright (c) 2013-2020 David Herberth + Copyright (c) 2013-2022 David Herberth Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in @@ -149,7 +149,7 @@ imgui The MIT License (MIT) -Copyright (c) 2014-2021 Omar Cornut +Copyright (c) 2014-2023 Omar Cornut Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -291,3 +291,27 @@ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------------------------------------- + +Vulkan Memory Allocator + +Copyright (c) 2017-2022 Advanced Micro Devices, Inc. All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file