From b3acf3100a908e7c2af79146eeab6905776ce7a7 Mon Sep 17 00:00:00 2001 From: chreden <4263940+chreden@users.noreply.github.com> Date: Thu, 29 May 2025 13:06:10 +0100 Subject: [PATCH 1/4] Models window Show models Start loading animations Take sampler state Use correct eye pos in model window Some mouse movement in models window --- trlevel/Level.h | 3 + trlevel/Level_tr1_pc.cpp | 10 +- trlevel/Level_tr1_psx.cpp | 18 +- trlevel/Level_tr2_pc.cpp | 12 +- trlevel/Level_tr2_psx.cpp | 24 +-- trlevel/Level_tr3_pc.cpp | 6 +- trlevel/Level_tr3_psx.cpp | 6 +- trlevel/Level_tr4_pc.cpp | 12 +- trlevel/Level_tr4_psx.cpp | 4 +- trlevel/Level_tr5_dc.cpp | 6 +- trlevel/Level_tr5_pc.cpp | 12 +- trlevel/Level_tr5_psx.cpp | 2 +- trlevel/trtypes.cpp | 26 +++ trlevel/trtypes.h | 2 + trview.app/ApplicationCreate.cpp | 11 + trview.app/Elements/ILevel.h | 2 + trview.app/Elements/Level.cpp | 12 +- trview.app/Elements/Level.h | 2 + trview.app/Geometry/ITransparencyBuffer.h | 7 +- trview.app/Geometry/Model/IModel.h | 2 + trview.app/Geometry/Model/IModelStorage.h | 1 + trview.app/Geometry/Model/ModelStorage.cpp | 69 ++++--- trview.app/Geometry/Model/ModelStorage.h | 1 + trview.app/Geometry/TransparencyBuffer.cpp | 4 +- trview.app/Geometry/TransparencyBuffer.h | 2 +- trview.app/Graphics/IMeshStorage.h | 3 + trview.app/Graphics/MeshStorage.cpp | 11 + trview.app/Graphics/MeshStorage.h | 1 + trview.app/Graphics/SelectionRenderer.cpp | 2 +- trview.app/Mocks/Elements/ILevel.h | 2 + trview.app/Mocks/Geometry/IModelStorage.h | 1 + .../Mocks/Geometry/ITransparencyBuffer.h | 2 +- trview.app/Mocks/Graphics/IMeshStorage.h | 1 + trview.app/Mocks/Windows/IModelsWindow.h | 19 ++ .../Mocks/Windows/IModelsWindowManager.h | 20 ++ trview.app/Resources/resource.h | 1 + trview.app/Resources/trview.app.rc | 1 + trview.app/Windows/Models/IModelsWindow.h | 19 ++ .../Windows/Models/IModelsWindowManager.h | 17 ++ trview.app/Windows/Models/ModelsWindow.cpp | 195 ++++++++++++++++++ trview.app/Windows/Models/ModelsWindow.h | 52 +++++ .../Windows/Models/ModelsWindowManager.cpp | 59 ++++++ .../Windows/Models/ModelsWindowManager.h | 28 +++ trview.app/trview.app.vcxproj.filters | 11 + 44 files changed, 609 insertions(+), 92 deletions(-) create mode 100644 trview.app/Mocks/Windows/IModelsWindow.h create mode 100644 trview.app/Mocks/Windows/IModelsWindowManager.h create mode 100644 trview.app/Windows/Models/IModelsWindow.h create mode 100644 trview.app/Windows/Models/IModelsWindowManager.h create mode 100644 trview.app/Windows/Models/ModelsWindow.cpp create mode 100644 trview.app/Windows/Models/ModelsWindow.h create mode 100644 trview.app/Windows/Models/ModelsWindowManager.cpp create mode 100644 trview.app/Windows/Models/ModelsWindowManager.h diff --git a/trlevel/Level.h b/trlevel/Level.h index c353783db..e2dbe49ba 100644 --- a/trlevel/Level.h +++ b/trlevel/Level.h @@ -282,6 +282,9 @@ namespace trlevel std::vector _entities; std::unordered_map _static_meshes; std::vector _ai_objects; + std::vector _animations; + std::vector _state_changes; + std::vector _anim_dispatches; uint16_t _lara_type{ 0u }; uint16_t _weather_type{ 0u }; diff --git a/trlevel/Level_tr1_pc.cpp b/trlevel/Level_tr1_pc.cpp index 37ab62076..1e1c5a5f9 100644 --- a/trlevel/Level_tr1_pc.cpp +++ b/trlevel/Level_tr1_pc.cpp @@ -468,8 +468,8 @@ namespace trlevel uint32_t num_animations = read(wad_file); skip(wad_file, num_animations * 26); - read_state_changes(activity, wad_file, callbacks); - read_anim_dispatches(activity, wad_file, callbacks); + _state_changes = read_state_changes(activity, wad_file, callbacks); + _anim_dispatches = read_anim_dispatches(activity, wad_file, callbacks); read_anim_commands(activity, wad_file, callbacks); _meshtree = read_meshtree(activity, wad_file, callbacks); _frames = read_frames(activity, wad_file, callbacks); @@ -685,9 +685,9 @@ namespace trlevel _floor_data = read_floor_data(activity, file, callbacks); _mesh_data = read_mesh_data(activity, file, callbacks); _mesh_pointers = read_mesh_pointers(activity, file, callbacks); - read_animations_tr1_3(activity, file, callbacks); - read_state_changes(activity, file, callbacks); - read_anim_dispatches(activity, file, callbacks); + _animations = convert_animations(read_animations_tr1_3(activity, file, callbacks)); + _state_changes = read_state_changes(activity, file, callbacks); + _anim_dispatches = read_anim_dispatches(activity, file, callbacks); read_anim_commands(activity, file, callbacks); _meshtree = read_meshtree(activity, file, callbacks); _frames = read_frames(activity, file, callbacks); diff --git a/trlevel/Level_tr1_psx.cpp b/trlevel/Level_tr1_psx.cpp index 93f17b9fb..0fdb6fe4a 100644 --- a/trlevel/Level_tr1_psx.cpp +++ b/trlevel/Level_tr1_psx.cpp @@ -305,9 +305,9 @@ namespace trlevel _floor_data = read_floor_data(activity, file, callbacks); _mesh_data = read_mesh_data(activity, file, callbacks); _mesh_pointers = read_mesh_pointers(activity, file, callbacks); - read_animations_tr1_3(activity, file, callbacks); - read_state_changes(activity, file, callbacks); - read_anim_dispatches(activity, file, callbacks); + _animations = convert_animations(read_animations_tr1_3(activity, file, callbacks)); + _state_changes = read_state_changes(activity, file, callbacks); + _anim_dispatches = read_anim_dispatches(activity, file, callbacks); read_anim_commands(activity, file, callbacks); _meshtree = read_meshtree(activity, file, callbacks); _frames = read_frames(activity, file, callbacks); @@ -434,9 +434,9 @@ namespace trlevel _floor_data = read_floor_data(activity, file, callbacks); _mesh_data = read_mesh_data(activity, file, callbacks); _mesh_pointers = read_mesh_pointers(activity, file, callbacks); - read_animations_tr1_3(activity, file, callbacks); - read_state_changes(activity, file, callbacks); - read_anim_dispatches(activity, file, callbacks); + _animations = convert_animations(read_animations_tr1_3(activity, file, callbacks)); + _state_changes = read_state_changes(activity, file, callbacks); + _anim_dispatches = read_anim_dispatches(activity, file, callbacks); read_anim_commands(activity, file, callbacks); _meshtree = read_meshtree(activity, file, callbacks); _frames = read_frames(activity, file, callbacks); @@ -484,11 +484,11 @@ namespace trlevel _mesh_data = read_mesh_data(activity, file, callbacks); _mesh_pointers = read_mesh_pointers(activity, file, callbacks); - uint32_t num_animations = read(file); + uint32_t num_animations = read(file); // TODO: Read animations skip(file, num_animations * 28); - read_state_changes(activity, file, callbacks); - read_anim_dispatches(activity, file, callbacks); + _state_changes = read_state_changes(activity, file, callbacks); + _anim_dispatches = read_anim_dispatches(activity, file, callbacks); read_anim_commands(activity, file, callbacks); _meshtree = read_meshtree(activity, file, callbacks); _frames = read_frames(activity, file, callbacks); diff --git a/trlevel/Level_tr2_pc.cpp b/trlevel/Level_tr2_pc.cpp index 763356c1e..5bc94c07d 100644 --- a/trlevel/Level_tr2_pc.cpp +++ b/trlevel/Level_tr2_pc.cpp @@ -105,9 +105,9 @@ namespace trlevel _floor_data = read_floor_data(activity, file, callbacks); _mesh_data = read_mesh_data(activity, file, callbacks); _mesh_pointers = read_mesh_pointers(activity, file, callbacks); - read_animations_tr1_3(activity, file, callbacks); - read_state_changes(activity, file, callbacks); - read_anim_dispatches(activity, file, callbacks); + _animations = convert_animations(read_animations_tr1_3(activity, file, callbacks)); + _state_changes = read_state_changes(activity, file, callbacks); + _anim_dispatches = read_anim_dispatches(activity, file, callbacks); read_anim_commands(activity, file, callbacks); _meshtree = read_meshtree(activity, file, callbacks); _frames = read_frames(activity, file, callbacks); @@ -162,9 +162,9 @@ namespace trlevel _floor_data = read_floor_data(activity, file, callbacks); _mesh_data = read_mesh_data(activity, file, callbacks); _mesh_pointers = read_mesh_pointers(activity, file, callbacks); - read_animations_tr1_3(activity, file, callbacks); - read_state_changes(activity, file, callbacks); - read_anim_dispatches(activity, file, callbacks); + _animations = convert_animations(read_animations_tr1_3(activity, file, callbacks)); + _state_changes = read_state_changes(activity, file, callbacks); + _anim_dispatches = read_anim_dispatches(activity, file, callbacks); read_anim_commands(activity, file, callbacks); _meshtree = read_meshtree(activity, file, callbacks); _frames = read_frames(activity, file, callbacks); diff --git a/trlevel/Level_tr2_psx.cpp b/trlevel/Level_tr2_psx.cpp index 25d52e342..a71435447 100644 --- a/trlevel/Level_tr2_psx.cpp +++ b/trlevel/Level_tr2_psx.cpp @@ -408,9 +408,9 @@ namespace trlevel _floor_data = read_floor_data(activity, file, callbacks); _mesh_data = read_mesh_data(activity, file, callbacks); _mesh_pointers = read_mesh_pointers(activity, file, callbacks); - read_animations_tr1_3(activity, file, callbacks); - read_state_changes(activity, file, callbacks); - read_anim_dispatches(activity, file, callbacks); + _animations = convert_animations(read_animations_tr1_3(activity, file, callbacks)); + _state_changes = read_state_changes(activity, file, callbacks); + _anim_dispatches = read_anim_dispatches(activity, file, callbacks); read_anim_commands(activity, file, callbacks); _meshtree = read_meshtree(activity, file, callbacks); _frames = read_frames(activity, file, callbacks); @@ -457,9 +457,9 @@ namespace trlevel _floor_data = read_floor_data(activity, file, callbacks); _mesh_data = read_mesh_data(activity, file, callbacks); _mesh_pointers = read_mesh_pointers(activity, file, callbacks); - read_animations_tr1_3(activity, file, callbacks); - read_state_changes(activity, file, callbacks); - read_anim_dispatches(activity, file, callbacks); + _animations = convert_animations(read_animations_tr1_3(activity, file, callbacks)); + _state_changes = read_state_changes(activity, file, callbacks); + _anim_dispatches = read_anim_dispatches(activity, file, callbacks); read_anim_commands(activity, file, callbacks); _meshtree = read_meshtree(activity, file, callbacks); _frames = read_frames(activity, file, callbacks); @@ -511,9 +511,9 @@ namespace trlevel _floor_data = read_floor_data(activity, file, callbacks); _mesh_data = read_mesh_data(activity, file, callbacks); _mesh_pointers = read_mesh_pointers(activity, file, callbacks); - read_animations_tr1_3(activity, file, callbacks); - read_state_changes(activity, file, callbacks); - read_anim_dispatches(activity, file, callbacks); + _animations = convert_animations(read_animations_tr1_3(activity, file, callbacks)); + _state_changes = read_state_changes(activity, file, callbacks); + _anim_dispatches = read_anim_dispatches(activity, file, callbacks); read_anim_commands(activity, file, callbacks); _meshtree = read_meshtree(activity, file, callbacks); _frames = read_frames(activity, file, callbacks); @@ -578,9 +578,9 @@ namespace trlevel _floor_data = read_floor_data(activity, file, callbacks); _mesh_data = read_mesh_data(activity, file, callbacks); _mesh_pointers = read_mesh_pointers(activity, file, callbacks); - read_animations_tr1_3(activity, file, callbacks); - read_state_changes(activity, file, callbacks); - read_anim_dispatches(activity, file, callbacks); + _animations = convert_animations(read_animations_tr1_3(activity, file, callbacks)); + _state_changes = read_state_changes(activity, file, callbacks); + _anim_dispatches = read_anim_dispatches(activity, file, callbacks); read_anim_commands(activity, file, callbacks); _meshtree = read_meshtree(activity, file, callbacks); _frames = read_frames(activity, file, callbacks); diff --git a/trlevel/Level_tr3_pc.cpp b/trlevel/Level_tr3_pc.cpp index 4a952e7ae..1c21bb57c 100644 --- a/trlevel/Level_tr3_pc.cpp +++ b/trlevel/Level_tr3_pc.cpp @@ -54,9 +54,9 @@ namespace trlevel _floor_data = read_floor_data(activity, file, callbacks); _mesh_data = read_mesh_data(activity, file, callbacks); _mesh_pointers = read_mesh_pointers(activity, file, callbacks); - read_animations_tr1_3(activity, file, callbacks); - read_state_changes(activity, file, callbacks); - read_anim_dispatches(activity, file, callbacks); + _animations = convert_animations(read_animations_tr1_3(activity, file, callbacks)); + _state_changes = read_state_changes(activity, file, callbacks); + _anim_dispatches = read_anim_dispatches(activity, file, callbacks); read_anim_commands(activity, file, callbacks); _meshtree = read_meshtree(activity, file, callbacks); _frames = read_frames(activity, file, callbacks); diff --git a/trlevel/Level_tr3_psx.cpp b/trlevel/Level_tr3_psx.cpp index a08c3c9f9..62d123ec3 100644 --- a/trlevel/Level_tr3_psx.cpp +++ b/trlevel/Level_tr3_psx.cpp @@ -262,9 +262,9 @@ namespace trlevel _mesh_data = read_mesh_data(activity, file, callbacks); _mesh_pointers = read_mesh_pointers(activity, file, callbacks); - read_animations_tr1_3(activity, file, callbacks); - read_state_changes(activity, file, callbacks); - read_anim_dispatches(activity, file, callbacks); + _animations = convert_animations(read_animations_tr1_3(activity, file, callbacks)); + _state_changes = read_state_changes(activity, file, callbacks); + _anim_dispatches = read_anim_dispatches(activity, file, callbacks); read_anim_commands(activity, file, callbacks); _meshtree = read_meshtree(activity, file, callbacks); _frames = read_frames(activity, file, callbacks); diff --git a/trlevel/Level_tr4_pc.cpp b/trlevel/Level_tr4_pc.cpp index 480d363ea..57577533f 100644 --- a/trlevel/Level_tr4_pc.cpp +++ b/trlevel/Level_tr4_pc.cpp @@ -119,9 +119,9 @@ namespace trlevel _floor_data = read_floor_data(activity, data_stream, callbacks); _mesh_data = read_mesh_data(activity, data_stream, callbacks); _mesh_pointers = read_mesh_pointers(activity, data_stream, callbacks); - read_animations_tr4_5(activity, data_stream, callbacks); - read_state_changes(activity, data_stream, callbacks); - read_anim_dispatches(activity, data_stream, callbacks); + _animations = read_animations_tr4_5(activity, data_stream, callbacks); + _state_changes = read_state_changes(activity, data_stream, callbacks); + _anim_dispatches = read_anim_dispatches(activity, data_stream, callbacks); read_anim_commands(activity, data_stream, callbacks); _meshtree = read_meshtree(activity, data_stream, callbacks); _frames = read_frames(activity, data_stream, callbacks); @@ -211,9 +211,9 @@ namespace trlevel _floor_data = read_floor_data(activity, file, callbacks); _mesh_data = read_mesh_data(activity, file, callbacks); _mesh_pointers = read_mesh_pointers(activity, file, callbacks); - read_animations_tr4_5(activity, file, callbacks); - read_state_changes(activity, file, callbacks); - read_anim_dispatches(activity, file, callbacks); + _animations = read_animations_tr4_5(activity, file, callbacks); + _state_changes = read_state_changes(activity, file, callbacks); + _anim_dispatches = read_anim_dispatches(activity, file, callbacks); read_anim_commands(activity, file, callbacks); _meshtree = read_meshtree(activity, file, callbacks); _frames = read_frames(activity, file, callbacks); diff --git a/trlevel/Level_tr4_psx.cpp b/trlevel/Level_tr4_psx.cpp index cb987a1cb..3f3f3da42 100644 --- a/trlevel/Level_tr4_psx.cpp +++ b/trlevel/Level_tr4_psx.cpp @@ -738,7 +738,7 @@ namespace trlevel _mesh_data = read_mesh_data(activity, file, info, callbacks); _mesh_pointers = read_mesh_pointers(activity, file, info, callbacks); - skip(file, info.animations_size); + skip(file, info.animations_size); // TODO: Load animations skip(file, info.state_changes_size); skip(file, info.dispatches_size); skip(file, info.commands_size); @@ -905,7 +905,7 @@ namespace trlevel _mesh_data = read_mesh_data(activity, file, info, callbacks); _mesh_pointers = read_mesh_pointers(activity, file, info, callbacks); - skip(file, info.animations_size); + skip(file, info.animations_size); // TODO: Read and store these skip(file, info.state_changes_size); skip(file, info.dispatches_size); skip(file, info.commands_size); diff --git a/trlevel/Level_tr5_dc.cpp b/trlevel/Level_tr5_dc.cpp index 26e875f30..61a29a378 100644 --- a/trlevel/Level_tr5_dc.cpp +++ b/trlevel/Level_tr5_dc.cpp @@ -217,9 +217,9 @@ namespace trlevel DreamcastPage page{ file }; _mesh_data = read_mesh_data(activity, file, callbacks); _mesh_pointers = read_mesh_pointers(activity, file, callbacks); - read_animations_tr4_5(activity, file, callbacks); - read_state_changes(activity, file, callbacks); - read_anim_dispatches(activity, file, callbacks); + _animations = read_animations_tr4_5(activity, file, callbacks); + _state_changes = read_state_changes(activity, file, callbacks); + _anim_dispatches = read_anim_dispatches(activity, file, callbacks); read_anim_commands(activity, file, callbacks); _meshtree = read_meshtree(activity, file, callbacks); _frames = read_frames(activity, file, callbacks); diff --git a/trlevel/Level_tr5_pc.cpp b/trlevel/Level_tr5_pc.cpp index 1144df8d1..ac9c15d03 100644 --- a/trlevel/Level_tr5_pc.cpp +++ b/trlevel/Level_tr5_pc.cpp @@ -222,9 +222,9 @@ namespace trlevel _floor_data = read_floor_data(activity, file, callbacks); _mesh_data = read_mesh_data(activity, file, callbacks); _mesh_pointers = read_mesh_pointers(activity, file, callbacks); - read_animations_tr4_5(activity, file, callbacks); - read_state_changes(activity, file, callbacks); - read_anim_dispatches(activity, file, callbacks); + _animations = read_animations_tr4_5(activity, file, callbacks); + _state_changes = read_state_changes(activity, file, callbacks); + _anim_dispatches = read_anim_dispatches(activity, file, callbacks); read_anim_commands(activity, file, callbacks); _meshtree = read_meshtree(activity, file, callbacks); _frames = read_frames(activity, file, callbacks); @@ -299,9 +299,9 @@ namespace trlevel _floor_data = read_floor_data(activity, file, callbacks); _mesh_data = read_mesh_data(activity, file, callbacks); _mesh_pointers = read_mesh_pointers(activity, file, callbacks); - read_animations_tr4_5(activity, file, callbacks); - read_state_changes(activity, file, callbacks); - read_anim_dispatches(activity, file, callbacks); + _animations = read_animations_tr4_5(activity, file, callbacks); + _state_changes = read_state_changes(activity, file, callbacks); + _anim_dispatches = read_anim_dispatches(activity, file, callbacks); read_anim_commands(activity, file, callbacks); _meshtree = read_meshtree(activity, file, callbacks); _frames = read_frames(activity, file, callbacks); diff --git a/trlevel/Level_tr5_psx.cpp b/trlevel/Level_tr5_psx.cpp index a48178f97..2d6293918 100644 --- a/trlevel/Level_tr5_psx.cpp +++ b/trlevel/Level_tr5_psx.cpp @@ -170,7 +170,7 @@ namespace trlevel _mesh_data = read_mesh_data(activity, file, info, callbacks); _mesh_pointers = read_mesh_pointers(activity, file, info, callbacks); - skip(file, info.animations_size); + skip(file, info.animations_size); // TODO: Load animations skip(file, info.state_changes_size); skip(file, info.dispatches_size); skip(file, info.commands_size); diff --git a/trlevel/trtypes.cpp b/trlevel/trtypes.cpp index fb9c9a904..847b1cb1c 100644 --- a/trlevel/trtypes.cpp +++ b/trlevel/trtypes.cpp @@ -201,4 +201,30 @@ namespace trlevel }; }) | std::ranges::to(); } + + std::vector convert_animations(std::vector animations) + { + return animations | std::views::transform([](auto&& a) -> tr4_animation + { + return + { + .FrameOffset = a.FrameOffset, + .FrameRate = a.FrameRate, + .FrameSize = a.FrameSize, + .State_ID = a.State_ID, + .Speed = a.Speed, + .Accel = a.Accel, + .SpeedLateral = 0, // TODO: Is this correct? + .AccelLateral = 0, // TODO: Is this correct? + .FrameStart = a.FrameStart, + .FrameEnd = a.FrameEnd, + .NextAnimation = a.NextAnimation, + .NextFrame = a.NextFrame, + .NumStateChanges = a.NumStateChanges, + .StateChangeOffset = a.StateChangeOffset, + .NumAnimCommands = a.NumAnimCommands, + .AnimCommand = a.AnimCommand, + }; + }) | std::ranges::to(); + } } \ No newline at end of file diff --git a/trlevel/trtypes.h b/trlevel/trtypes.h index cdb440d3e..54a1bd9c4 100644 --- a/trlevel/trtypes.h +++ b/trlevel/trtypes.h @@ -1003,6 +1003,8 @@ namespace trlevel std::vector convert_tr3_psx_room_triangles(std::vector triangles, uint16_t total_vertices); + std::vector convert_animations(std::vector animations); + constexpr LightType convert(LightTypeTR3 type); } diff --git a/trview.app/ApplicationCreate.cpp b/trview.app/ApplicationCreate.cpp index 06ef73c41..db4eb4390 100644 --- a/trview.app/ApplicationCreate.cpp +++ b/trview.app/ApplicationCreate.cpp @@ -68,6 +68,7 @@ #include "Windows/TriggersWindow.h" #include "Windows/Viewer.h" #include "Windows/Log/LogWindow.h" +#include "Windows/Models/ModelsWindow.h" #include "UI/DX11ImGuiBackend.h" #include "Windows/Textures/TexturesWindow.h" #include "Windows/CameraSink/CameraSinkWindow.h" @@ -497,6 +498,12 @@ namespace trview pack_window->initialise(); return pack_window; }; + auto models_window_source = [=]() + { + auto models_window = std::make_shared(files, dialogs, messaging); + models_window->initialise(); + return models_window; + }; auto windows = std::make_shared(window, shortcuts); windows->register_window("About", about_window_source); @@ -514,6 +521,7 @@ namespace trview windows->register_window("Statics", statics_window_source); windows->register_window("Textures", textures_window_source); windows->register_window("Triggers", triggers_window_source); + windows->register_window("Models", models_window_source); auto viewer_ui = std::make_shared( window, @@ -550,6 +558,9 @@ namespace trview auto file_menu = std::make_shared(window, shortcuts, dialogs, files, level_name_source, messaging); messaging->add_recipient(file_menu); + auto transparency_buffer_source = [=](auto&& lts) { return std::make_unique(device, lts); }; + auto meshes_window_source = [=]() { return std::make_shared(device, render_target_source, shader_storage, buffer_source, transparency_buffer_source, sampler_source, std::make_unique(window, std::make_unique(window))); }; + auto application = std::make_shared( window, std::make_unique(window), diff --git a/trview.app/Elements/ILevel.h b/trview.app/Elements/ILevel.h index a65047a67..d438ceaed 100644 --- a/trview.app/Elements/ILevel.h +++ b/trview.app/Elements/ILevel.h @@ -154,6 +154,8 @@ namespace trview virtual std::string hash() const = 0; virtual std::vector> flybys() const = 0; virtual void update(float delta) = 0; + virtual std::weak_ptr model_storage() const = 0; + virtual std::weak_ptr level_texture_storage() const = 0; // Event raised when the level needs to change the alternate mode. Event on_alternate_mode_selected; diff --git a/trview.app/Elements/Level.cpp b/trview.app/Elements/Level.cpp index baf41d681..835f193d9 100644 --- a/trview.app/Elements/Level.cpp +++ b/trview.app/Elements/Level.cpp @@ -455,7 +455,7 @@ namespace trview _pixel_shader_data->apply(context, graphics::IBuffer::ApplyTo::PS); _room_sampler_state->apply(); - _transparency->render(camera); + _transparency->render(camera.view_projection()); } void Level::render_selected_item(const ICamera& camera) @@ -1647,6 +1647,16 @@ namespace trview return _flybys | std::ranges::to>>(); } + std::weak_ptr Level::model_storage() const + { + return _model_storage; + } + + std::weak_ptr Level::level_texture_storage() const + { + return _texture_storage; + } + void Level::update(float delta) { if (!_show_animation) diff --git a/trview.app/Elements/Level.h b/trview.app/Elements/Level.h index b71de1c2d..7e2835f6b 100644 --- a/trview.app/Elements/Level.h +++ b/trview.app/Elements/Level.h @@ -145,6 +145,8 @@ namespace trview void update(float delta) override; void set_show_animation(bool show) override; void receive_message(const Message& message) override; + std::weak_ptr model_storage() const override; + std::weak_ptr level_texture_storage() const override; private: void generate_rooms(const trlevel::ILevel& level, const IRoom::Source& room_source, const IMeshStorage& mesh_storage); void generate_triggers(const ITrigger::Source& trigger_source); diff --git a/trview.app/Geometry/ITransparencyBuffer.h b/trview.app/Geometry/ITransparencyBuffer.h index 6698d3ada..afa1d1175 100644 --- a/trview.app/Geometry/ITransparencyBuffer.h +++ b/trview.app/Geometry/ITransparencyBuffer.h @@ -5,8 +5,11 @@ namespace trview { + struct ILevelTextureStorage; struct ITransparencyBuffer { + using Source = std::function(const std::weak_ptr&)>; + virtual ~ITransparencyBuffer() = 0; // Add a triangle to the transparency buffer. The triangle will be added to the end // of the collection. @@ -19,10 +22,10 @@ namespace trview virtual void sort(const DirectX::SimpleMath::Vector3& eye_position) = 0; /// Render the accumulated transparent triangles. Sort should be called before this function is called. - /// @param camera The current camera. + /// @param view_project The view projection. /// @param texture_storage Texture storage for the level. /// @param ignore_blend Optional. Set to true to render this without transparency. - virtual void render(const ICamera& camera, bool ignore_blend = false) = 0; + virtual void render(const DirectX::SimpleMath::Matrix& view_projection, bool ignore_blend = false) = 0; // Reset the triangles buffer. virtual void reset() = 0; diff --git a/trview.app/Geometry/Model/IModel.h b/trview.app/Geometry/Model/IModel.h index ffb4003c8..83ebcdbd5 100644 --- a/trview.app/Geometry/Model/IModel.h +++ b/trview.app/Geometry/Model/IModel.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "../PickResult.h" namespace trview diff --git a/trview.app/Geometry/Model/IModelStorage.h b/trview.app/Geometry/Model/IModelStorage.h index 3838f0606..ad2b446be 100644 --- a/trview.app/Geometry/Model/IModelStorage.h +++ b/trview.app/Geometry/Model/IModelStorage.h @@ -7,5 +7,6 @@ namespace trview { virtual ~IModelStorage() = 0; virtual std::weak_ptr find_by_type_id(uint16_t type_id) const = 0; + virtual std::vector> models() const = 0; }; } diff --git a/trview.app/Geometry/Model/ModelStorage.cpp b/trview.app/Geometry/Model/ModelStorage.cpp index 1d9e351f7..fefb2651e 100644 --- a/trview.app/Geometry/Model/ModelStorage.cpp +++ b/trview.app/Geometry/Model/ModelStorage.cpp @@ -1,5 +1,6 @@ #include "ModelStorage.h" #include "../../Graphics/IMeshStorage.h" +#include namespace trview { @@ -69,6 +70,39 @@ namespace trview return false; } + std::vector> load_meshes( + const trlevel::tr_model& model, + const trlevel::PlatformAndVersion& version, + const std::shared_ptr mesh_storage) + { + std::vector> meshes; + if (version.platform == trlevel::Platform::PSX && equals_any(version.version, trlevel::LevelVersion::Tomb4, trlevel::LevelVersion::Tomb5)) + { + const uint32_t end_pointer = static_cast(model.StartingMesh + model.NumMeshes * 2); + for (uint32_t mesh_pointer = model.StartingMesh; mesh_pointer < end_pointer; mesh_pointer += 2) + { + auto mesh = mesh_storage->mesh(mesh_pointer); + if (mesh) + { + meshes.push_back(mesh); + } + } + } + else + { + const uint32_t end_pointer = static_cast(model.StartingMesh + model.NumMeshes); + for (uint32_t mesh_pointer = model.StartingMesh; mesh_pointer < end_pointer; ++mesh_pointer) + { + auto mesh = mesh_storage->mesh(mesh_pointer); + if (mesh) + { + meshes.push_back(mesh); + } + } + } + return meshes; + } + std::vector load_transforms( const trlevel::tr_model& model, const trlevel::ILevel& level) @@ -176,38 +210,12 @@ namespace trview for (uint32_t i = 0; i < level.num_models(); ++i) { const auto model = level.get_model(i); - if (model.NumMeshes > 0xff00) + if (model.NumMeshes > 0xff00 || model.NumMeshes == 0) { continue; } - std::vector> meshes; - - if (version.platform == trlevel::Platform::PSX && equals_any(version.version, trlevel::LevelVersion::Tomb4, trlevel::LevelVersion::Tomb5)) - { - const uint32_t end_pointer = static_cast(model.StartingMesh + model.NumMeshes * 2); - for (uint32_t mesh_pointer = model.StartingMesh; mesh_pointer < end_pointer; mesh_pointer += 2) - { - auto mesh = mesh_storage->mesh(mesh_pointer); - if (mesh) - { - meshes.push_back(mesh); - } - } - } - else - { - const uint32_t end_pointer = static_cast(model.StartingMesh + model.NumMeshes); - for (uint32_t mesh_pointer = model.StartingMesh; mesh_pointer < end_pointer; ++mesh_pointer) - { - auto mesh = mesh_storage->mesh(mesh_pointer); - if (mesh) - { - meshes.push_back(mesh); - } - } - } - + auto meshes = load_meshes(model, version, mesh_storage); auto transforms = load_transforms(model, level); _models.push_back(model_source(model, meshes, transforms)); } @@ -225,4 +233,9 @@ namespace trview } return {}; } + + std::vector> ModelStorage::models() const + { + return _models | std::ranges::to>>(); + } } diff --git a/trview.app/Geometry/Model/ModelStorage.h b/trview.app/Geometry/Model/ModelStorage.h index f2a989ab3..01b16f40d 100644 --- a/trview.app/Geometry/Model/ModelStorage.h +++ b/trview.app/Geometry/Model/ModelStorage.h @@ -16,6 +16,7 @@ namespace trview const trlevel::ILevel& level); virtual ~ModelStorage() = default; std::weak_ptr find_by_type_id(uint16_t type_id) const override; + std::vector> models() const override; private: void load_models(const std::shared_ptr& mesh_storage, const IModel::Source& model_source, diff --git a/trview.app/Geometry/TransparencyBuffer.cpp b/trview.app/Geometry/TransparencyBuffer.cpp index 7bfe2e7fa..970940420 100644 --- a/trview.app/Geometry/TransparencyBuffer.cpp +++ b/trview.app/Geometry/TransparencyBuffer.cpp @@ -77,7 +77,7 @@ namespace trview complete(); } - void TransparencyBuffer::render(const ICamera& camera, bool ignore_blend) + void TransparencyBuffer::render(const Matrix& view_projection, bool ignore_blend) { if (!_vertices.size()) { @@ -101,7 +101,7 @@ namespace trview D3D11_MAPPED_SUBRESOURCE mapped_resource; memset(&mapped_resource, 0, sizeof(mapped_resource)); - MeshData data{ camera.view_projection(), Color(1,1,1,1), Vector4::Zero }; + MeshData data{ view_projection, Color(1,1,1,1), Vector4::Zero }; context->Map(_matrix_buffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped_resource); memcpy(mapped_resource.pData, &data, sizeof(data)); diff --git a/trview.app/Geometry/TransparencyBuffer.h b/trview.app/Geometry/TransparencyBuffer.h index b0ad792cc..cff2b8bc3 100644 --- a/trview.app/Geometry/TransparencyBuffer.h +++ b/trview.app/Geometry/TransparencyBuffer.h @@ -37,7 +37,7 @@ namespace trview /// @param camera The current camera. /// @param texture_storage Texture storage for the level. /// @param ignore_blend Optional. Set to true to render this without transparency. - void render(const ICamera& camera, bool ignore_blend = false) override; + void render(const DirectX::SimpleMath::Matrix& view_projection, bool ignore_blend = false) override; // Reset the triangles buffer. void reset() override; diff --git a/trview.app/Graphics/IMeshStorage.h b/trview.app/Graphics/IMeshStorage.h index f7572b930..ed0a402c0 100644 --- a/trview.app/Graphics/IMeshStorage.h +++ b/trview.app/Graphics/IMeshStorage.h @@ -1,5 +1,7 @@ #pragma once +#include +#include #include #include #include @@ -12,5 +14,6 @@ namespace trview using Source = std::function(const trlevel::ILevel&, const ILevelTextureStorage&)>; virtual ~IMeshStorage() = 0; virtual std::shared_ptr mesh(uint32_t mesh_pointer) const = 0; + virtual std::map> meshes() const = 0; }; } diff --git a/trview.app/Graphics/MeshStorage.cpp b/trview.app/Graphics/MeshStorage.cpp index b7895aca5..97b245e92 100644 --- a/trview.app/Graphics/MeshStorage.cpp +++ b/trview.app/Graphics/MeshStorage.cpp @@ -1,4 +1,5 @@ #include "MeshStorage.h" +#include namespace trview { @@ -24,4 +25,14 @@ namespace trview } return nullptr; } + + std::map> MeshStorage::meshes() const + { + std::map> meshes; + for (const auto& m : _meshes) + { + meshes[m.first] = m.second; + } + return meshes; + } } diff --git a/trview.app/Graphics/MeshStorage.h b/trview.app/Graphics/MeshStorage.h index 2e87e8f86..eab774b6a 100644 --- a/trview.app/Graphics/MeshStorage.h +++ b/trview.app/Graphics/MeshStorage.h @@ -17,6 +17,7 @@ namespace trview explicit MeshStorage(const IMesh::Source& mesh_source, const trlevel::ILevel& level, const ILevelTextureStorage& texture_storage); virtual ~MeshStorage() = default; virtual std::shared_ptr mesh(uint32_t mesh_pointer) const override; + virtual std::map> meshes() const override; private: mutable std::unordered_map> _meshes; }; diff --git a/trview.app/Graphics/SelectionRenderer.cpp b/trview.app/Graphics/SelectionRenderer.cpp index 0d50d1496..8eea6019a 100644 --- a/trview.app/Graphics/SelectionRenderer.cpp +++ b/trview.app/Graphics/SelectionRenderer.cpp @@ -150,7 +150,7 @@ namespace trview _transparency->reset(); selected_item.get_transparent_triangles(*_transparency, camera, IRenderable::SelectionFill); _transparency->sort(camera.rendering_position()); - _transparency->render(camera, true); + _transparency->render(camera.view_projection(), true); selected_item.set_visible(was_visible); } diff --git a/trview.app/Mocks/Elements/ILevel.h b/trview.app/Mocks/Elements/ILevel.h index dedb475c7..1ce22f4fb 100644 --- a/trview.app/Mocks/Elements/ILevel.h +++ b/trview.app/Mocks/Elements/ILevel.h @@ -91,6 +91,8 @@ namespace trview MOCK_METHOD(void, update, (float), (override)); MOCK_METHOD(void, set_show_animation, (bool), (override)); MOCK_METHOD(void, receive_message, (const Message&), (override)); + MOCK_METHOD(std::weak_ptr, model_storage, (), (const, override)); + MOCK_METHOD(std::weak_ptr, level_texture_storage, (), (const, override)); std::shared_ptr with_version(trlevel::LevelVersion version) { diff --git a/trview.app/Mocks/Geometry/IModelStorage.h b/trview.app/Mocks/Geometry/IModelStorage.h index 173e6693f..67a91efe0 100644 --- a/trview.app/Mocks/Geometry/IModelStorage.h +++ b/trview.app/Mocks/Geometry/IModelStorage.h @@ -11,6 +11,7 @@ namespace trview MockModelStorage(); virtual ~MockModelStorage(); MOCK_METHOD(std::weak_ptr, find_by_type_id, (uint16_t), (const, override)); + MOCK_METHOD(std::vector>, models, (), (const, override)); }; } } \ No newline at end of file diff --git a/trview.app/Mocks/Geometry/ITransparencyBuffer.h b/trview.app/Mocks/Geometry/ITransparencyBuffer.h index cd2eea345..39957d740 100644 --- a/trview.app/Mocks/Geometry/ITransparencyBuffer.h +++ b/trview.app/Mocks/Geometry/ITransparencyBuffer.h @@ -12,7 +12,7 @@ namespace trview virtual ~MockTransparencyBuffer(); MOCK_METHOD(void, add, (const Triangle&), (override)); MOCK_METHOD(void, sort, (const DirectX::SimpleMath::Vector3&), (override)); - MOCK_METHOD(void, render, (const ICamera&, bool), (override)); + MOCK_METHOD(void, render, (const DirectX::SimpleMath::Matrix&, bool), (override)); MOCK_METHOD(void, reset, (), (override)); }; } diff --git a/trview.app/Mocks/Graphics/IMeshStorage.h b/trview.app/Mocks/Graphics/IMeshStorage.h index 0368601ec..93239f110 100644 --- a/trview.app/Mocks/Graphics/IMeshStorage.h +++ b/trview.app/Mocks/Graphics/IMeshStorage.h @@ -11,6 +11,7 @@ namespace trview MockMeshStorage(); virtual ~MockMeshStorage(); MOCK_METHOD(std::shared_ptr, mesh, (uint32_t), (const, override)); + MOCK_METHOD((std::map>), meshes, (), (const, override)); }; } } \ No newline at end of file diff --git a/trview.app/Mocks/Windows/IModelsWindow.h b/trview.app/Mocks/Windows/IModelsWindow.h new file mode 100644 index 000000000..35bf8cbd8 --- /dev/null +++ b/trview.app/Mocks/Windows/IModelsWindow.h @@ -0,0 +1,19 @@ +#pragma once + +#include "../../Windows/Models/IModelsWindow.h" + +namespace trview +{ + namespace mocks + { + struct MockModelsWindow : public IModelsWindow + { + MockModelsWindow(); + virtual ~MockModelsWindow(); + MOCK_METHOD(void, render, (), (override)); + MOCK_METHOD(void, set_model_storage, (const std::weak_ptr&), (override)); + MOCK_METHOD(void, update, (float), (override)); + MOCK_METHOD(void, set_number, (int32_t), (override)); + }; + } +} diff --git a/trview.app/Mocks/Windows/IModelsWindowManager.h b/trview.app/Mocks/Windows/IModelsWindowManager.h new file mode 100644 index 000000000..1fc5f41b1 --- /dev/null +++ b/trview.app/Mocks/Windows/IModelsWindowManager.h @@ -0,0 +1,20 @@ +#pragma once + +#include "../../Windows/Models/IModelsWindowManager.h" + +namespace trview +{ + namespace mocks + { + struct MockModelsWindowManager : public IModelsWindowManager + { + MockModelsWindowManager(); + virtual ~MockModelsWindowManager(); + MOCK_METHOD(std::weak_ptr, create_window, (), (override)); + MOCK_METHOD(void, render, (), (override)); + MOCK_METHOD(void, set_level_texture_storage, (const std::weak_ptr&), (override)); + MOCK_METHOD(void, set_model_storage, (const std::weak_ptr&), (override)); + MOCK_METHOD(void, update, (float), (override)); + }; + } +} diff --git a/trview.app/Resources/resource.h b/trview.app/Resources/resource.h index 09dff1b60..48eb3e2e7 100644 --- a/trview.app/Resources/resource.h +++ b/trview.app/Resources/resource.h @@ -54,6 +54,7 @@ #define ID_WINDOWS_DIFF 33031 #define ID_WINDOWS_PACK 33032 #define IDR_LEVEL_HASHES 33033 +#define ID_WINDOWS_MODELS 33033 // Next default values for new objects // diff --git a/trview.app/Resources/trview.app.rc b/trview.app/Resources/trview.app.rc index 9698b36d6..7d57e812a 100644 --- a/trview.app/Resources/trview.app.rc +++ b/trview.app/Resources/trview.app.rc @@ -72,6 +72,7 @@ BEGIN MENUITEM "&Lights\tCtrl+L" ID_WINDOWS_LIGHTS MENUITEM "Camera/Sink\tCtrl+K" ID_WINDOWS_CAMERA_SINK MENUITEM "Log" ID_WINDOWS_LOG + MENUITEM "Models" ID_WINDOWS_MODELS MENUITEM "Textures" ID_WINDOWS_TEXTURES MENUITEM "Console\tF11" ID_WINDOWS_CONSOLE MENUITEM "Plugins\tCtrl+P" ID_WINDOWS_PLUGINS diff --git a/trview.app/Windows/Models/IModelsWindow.h b/trview.app/Windows/Models/IModelsWindow.h new file mode 100644 index 000000000..4426180ee --- /dev/null +++ b/trview.app/Windows/Models/IModelsWindow.h @@ -0,0 +1,19 @@ +#pragma once + +namespace trview +{ + struct ILevelTextureStorage; + struct IModelStorage; + struct IModelsWindow + { + using Source = std::function()>; + virtual ~IModelsWindow() = 0; + virtual void render() = 0; + virtual void set_level_texture_storage(const std::weak_ptr& level_texture_storage) = 0; + virtual void set_model_storage(const std::weak_ptr& model_storage) = 0; + virtual void set_number(int32_t number) = 0; + virtual void update(float delta) = 0; + + Event<> on_window_closed; + }; +} diff --git a/trview.app/Windows/Models/IModelsWindowManager.h b/trview.app/Windows/Models/IModelsWindowManager.h new file mode 100644 index 000000000..3b55486ae --- /dev/null +++ b/trview.app/Windows/Models/IModelsWindowManager.h @@ -0,0 +1,17 @@ +#pragma once + +namespace trview +{ + struct ILevelTextureStorage; + struct IModelStorage; + struct IModelsWindow; + struct IModelsWindowManager + { + virtual ~IModelsWindowManager() = 0; + virtual std::weak_ptr create_window() = 0; + virtual void render() = 0; + virtual void set_level_texture_storage(const std::weak_ptr& level_texture_storage) = 0; + virtual void set_model_storage(const std::weak_ptr& model_storage) = 0; + virtual void update(float delta) = 0; + }; +} diff --git a/trview.app/Windows/Models/ModelsWindow.cpp b/trview.app/Windows/Models/ModelsWindow.cpp new file mode 100644 index 000000000..e080d5d27 --- /dev/null +++ b/trview.app/Windows/Models/ModelsWindow.cpp @@ -0,0 +1,195 @@ +#include "ModelsWindow.h" +#include "../../Geometry/Model/IModelStorage.h" +#include "../../Geometry/Model/IModel.h" + +#include +#include + +namespace trview +{ + namespace + { +#pragma warning(push) +#pragma warning(disable : 4324) +#pragma pack(push, 16) + __declspec(align(16)) struct PixelShaderData + { + bool disable_transparency; + }; +#pragma pack(pop) +#pragma warning(pop) + } + + IModelsWindow::~IModelsWindow() + { + } + + ModelsWindow::ModelsWindow(const std::shared_ptr& device, + const graphics::IRenderTarget::SizeSource& render_target_source, + const std::shared_ptr& shader_storage, + const graphics::IBuffer::ConstantSource& buffer_source, + ITransparencyBuffer::Source transparency_buffer_source, + const graphics::ISamplerState::Source& sampler_source, + std::unique_ptr mouse) + : _device(device), _render_target(render_target_source(512, 512, graphics::IRenderTarget::DepthStencilMode::Enabled)), _transparency_buffer_source(transparency_buffer_source), + _sampler_state(sampler_source(graphics::ISamplerState::AddressMode::Clamp)), _mouse(std::move(mouse)) + { + _vertex_shader = shader_storage->get("level_vertex_shader"); + _pixel_shader = shader_storage->get("level_pixel_shader"); + + D3D11_DEPTH_STENCIL_DESC stencil_desc; + memset(&stencil_desc, 0, sizeof(stencil_desc)); + stencil_desc.DepthEnable = true; + stencil_desc.DepthWriteMask = D3D11_DEPTH_WRITE_MASK_ALL; + stencil_desc.DepthFunc = D3D11_COMPARISON_LESS; + stencil_desc.StencilEnable = true; + stencil_desc.StencilReadMask = 0xFF; + stencil_desc.StencilWriteMask = 0xFF; + stencil_desc.FrontFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; + stencil_desc.FrontFace.StencilDepthFailOp = D3D11_STENCIL_OP_INCR; + stencil_desc.FrontFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; + stencil_desc.FrontFace.StencilFunc = D3D11_COMPARISON_ALWAYS; + stencil_desc.BackFace.StencilFailOp = D3D11_STENCIL_OP_KEEP; + stencil_desc.BackFace.StencilDepthFailOp = D3D11_STENCIL_OP_DECR; + stencil_desc.BackFace.StencilPassOp = D3D11_STENCIL_OP_KEEP; + stencil_desc.BackFace.StencilFunc = D3D11_COMPARISON_ALWAYS; + _depth_stencil_state = device->create_depth_stencil_state(stencil_desc); + + _pixel_shader_data = buffer_source(sizeof(PixelShaderData)); + + _token_store += _mouse->mouse_down += [&](auto button) { _camera_input.mouse_down(button); }; + _token_store += _mouse->mouse_up += [&](auto button) { _camera_input.mouse_up(button); }; + _token_store += _mouse->mouse_move += [&](long x, long y) { _camera_input.mouse_move(x, y); }; + _token_store += _camera_input.on_rotate += [&](float x, float y) + { + if (_mouse_over) + { + const float low_sensitivity = 200.0f; + const float high_sensitivity = 25.0f; + const float sensitivity = low_sensitivity + (high_sensitivity - low_sensitivity); + _rotation += x / sensitivity; + _rotation_pitch += y / sensitivity; + } + }; + } + + void ModelsWindow::render() + { + if (!render_meshes_window()) + { + on_window_closed(); + return; + } + } + + void ModelsWindow::set_level_texture_storage(const std::weak_ptr& level_texture_storage) + { + _transparency_buffer = _transparency_buffer_source(level_texture_storage); + _selected_model.reset(); + } + + void ModelsWindow::set_model_storage(const std::weak_ptr& model_storage) + { + _model_storage = model_storage; + } + + void ModelsWindow::set_number(int32_t number) + { + _id = std::format("Models {}", number); + } + + void ModelsWindow::update(float) + { + } + + bool ModelsWindow::render_meshes_window() + { + _mouse_over = false; + + using namespace DirectX::SimpleMath; + + auto selected_model = _selected_model.lock(); + + auto context = _device->context(); + context->OMSetDepthStencilState(_depth_stencil_state.Get(), 1); + _render_target->clear(Color(0.2f, 0.2f, 0.2f)); + { + context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + _vertex_shader->apply(context); + _pixel_shader->apply(context); + + graphics::RenderTargetStore rs_store(context); + graphics::ViewportStore vp_store(context); + _render_target->apply(); + + if (selected_model) + { + _sampler_state->apply(); + + const auto box = selected_model->bounding_box(); + const float ex = Vector3(box.Extents).Length(); + const auto camera_pos = Vector3(0, box.Center.y - 0.25f, ex * 3.5f); + + Matrix world = Matrix::Identity; + Matrix camera_rotation = Matrix::CreateFromYawPitchRoll(_rotation, _rotation_pitch, 0); + + const auto eye = Vector3::Transform(camera_pos, camera_rotation); + + Matrix view = Matrix::CreateLookAt(eye, box.Center, Vector3(0, -1, 0)); + Matrix projection = Matrix::CreatePerspectiveFieldOfView(DirectX::XM_PIDIV4, 1.0f, 0.1f, 1000.0f); + Matrix view_projection = view * projection; + + graphics::set_data(*_pixel_shader_data, context, PixelShaderData{ false }); + _pixel_shader_data->apply(context, graphics::IBuffer::ApplyTo::PS); + + selected_model->render(world, view_projection, Colour::White); + + _transparency_buffer->reset(); + selected_model->render_transparency(world, *_transparency_buffer, Colour::White); + _transparency_buffer->sort(eye); + + graphics::set_data(*_pixel_shader_data, context, PixelShaderData{ false }); + _pixel_shader_data->apply(context, graphics::IBuffer::ApplyTo::PS); + _transparency_buffer->render(view_projection); + } + } + + bool stay_open = true; + ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(540, 500)); + if (ImGui::Begin(_id.c_str(), &stay_open)) + { + if (ImGui::BeginTable("Meshes List", 1, ImGuiTableFlags_ScrollY, ImVec2(100, 0))) + { + ImGui::TableSetupColumn("ID"); + ImGui::TableSetupScrollFreeze(1, 1); + ImGui::TableHeadersRow(); + + if (auto model_storage = _model_storage.lock()) + { + for (const auto model : model_storage->models()) + { + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + + auto model_ptr = model.lock(); + bool selected = model_ptr == selected_model; + if (ImGui::Selectable(std::format("{}", model_ptr->type_id()).c_str(), &selected, ImGuiSelectableFlags_SelectOnNav)) + { + _selected_model = model; + } + } + } + ImGui::EndTable(); + } + + ImGui::SameLine(); + + const auto texture = _render_target->texture(); + ImGui::Image(texture.view().Get(), ImVec2(texture.size().width, texture.size().height)); + _mouse_over = ImGui::GetIO().WantCaptureMouse && ImGui::IsItemHovered(); + } + ImGui::End(); + ImGui::PopStyleVar(); + return stay_open; + } +} diff --git a/trview.app/Windows/Models/ModelsWindow.h b/trview.app/Windows/Models/ModelsWindow.h new file mode 100644 index 000000000..b9d7564a1 --- /dev/null +++ b/trview.app/Windows/Models/ModelsWindow.h @@ -0,0 +1,52 @@ +#pragma once + +#include "IModelsWindow.h" +#include +#include +#include "../../Geometry/ITransparencyBuffer.h" +#include +#include +#include "../../Camera/CameraInput.h" + +namespace trview +{ + struct IModel; + class ModelsWindow final : public IModelsWindow + { + public: + ModelsWindow(const std::shared_ptr& device, + const graphics::IRenderTarget::SizeSource& render_target_source, + const std::shared_ptr& shader_storage, + const graphics::IBuffer::ConstantSource& buffer_source, + ITransparencyBuffer::Source transparency_buffer_source, + const graphics::ISamplerState::Source& sampler_source, + std::unique_ptr mouse); + virtual ~ModelsWindow() = default; + void render() override; + void set_level_texture_storage(const std::weak_ptr& level_texture_storage) override; + void set_model_storage(const std::weak_ptr& model_storage) override; + void set_number(int32_t number) override; + void update(float delta) override; + private: + bool render_meshes_window(); + + std::string _id{ "Models 0" }; + std::weak_ptr _model_storage; + std::weak_ptr _selected_model; + std::unique_ptr _render_target; + std::shared_ptr _device; + Microsoft::WRL::ComPtr _depth_stencil_state; + graphics::IShader* _vertex_shader; + graphics::IShader* _pixel_shader; + float _rotation{ 0.0f }; + float _rotation_pitch{ 0.0f }; + std::unique_ptr _pixel_shader_data; + std::unique_ptr _transparency_buffer; + ITransparencyBuffer::Source _transparency_buffer_source; + std::shared_ptr _sampler_state; + std::unique_ptr _mouse; + TokenStore _token_store; + CameraInput _camera_input; + bool _mouse_over{ false }; + }; +} diff --git a/trview.app/Windows/Models/ModelsWindowManager.cpp b/trview.app/Windows/Models/ModelsWindowManager.cpp new file mode 100644 index 000000000..0f267caa6 --- /dev/null +++ b/trview.app/Windows/Models/ModelsWindowManager.cpp @@ -0,0 +1,59 @@ +#include "ModelsWindowManager.h" +#include "../../Resources/resource.h" + +namespace trview +{ + IModelsWindowManager::~IModelsWindowManager() + { + } + + ModelsWindowManager::ModelsWindowManager(const Window& window, const IModelsWindow::Source& models_window_source) + : MessageHandler(window), _models_window_source(models_window_source) + { + } + + std::weak_ptr ModelsWindowManager::create_window() + { + auto models_window = _models_window_source(); + models_window->set_level_texture_storage(_level_texture_storage); + models_window->set_model_storage(_model_storage); + return add_window(models_window); + } + + std::optional ModelsWindowManager::process_message(UINT message, WPARAM wParam, LPARAM) + { + if (message == WM_COMMAND && LOWORD(wParam) == ID_WINDOWS_MODELS) + { + create_window(); + } + return {}; + } + + void ModelsWindowManager::render() + { + WindowManager::render(); + } + + void ModelsWindowManager::set_level_texture_storage(const std::weak_ptr& level_texture_storage) + { + _level_texture_storage = level_texture_storage; + for (auto& window : _windows) + { + window.second->set_level_texture_storage(_level_texture_storage); + } + } + + void ModelsWindowManager::set_model_storage(const std::weak_ptr& model_storage) + { + _model_storage = model_storage; + for (auto& window : _windows) + { + window.second->set_model_storage(_model_storage); + } + } + + void ModelsWindowManager::update(float delta) + { + WindowManager::update(delta); + } +} diff --git a/trview.app/Windows/Models/ModelsWindowManager.h b/trview.app/Windows/Models/ModelsWindowManager.h new file mode 100644 index 000000000..14453b135 --- /dev/null +++ b/trview.app/Windows/Models/ModelsWindowManager.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include "../WindowManager.h" +#include "IModelsWindowManager.h" +#include "IModelsWindow.h" + +namespace trview +{ + struct ILevelTextureStorage; + class ModelsWindowManager final : public IModelsWindowManager, public WindowManager, public MessageHandler + { + public: + explicit ModelsWindowManager(const Window& window, const IModelsWindow::Source& models_window_source); + virtual ~ModelsWindowManager() = default; + virtual std::weak_ptr create_window() override; + std::optional process_message(UINT message, WPARAM wParam, LPARAM lParam) override; + void render() override; + void set_level_texture_storage(const std::weak_ptr& level_texture_storage) override; + void set_model_storage(const std::weak_ptr& model_storage) override; + void update(float delta) override; + private: + IModelsWindow::Source _models_window_source; + std::weak_ptr _level_texture_storage; + std::weak_ptr _model_storage; + }; +} diff --git a/trview.app/trview.app.vcxproj.filters b/trview.app/trview.app.vcxproj.filters index ef3c040e0..48c85a065 100644 --- a/trview.app/trview.app.vcxproj.filters +++ b/trview.app/trview.app.vcxproj.filters @@ -117,7 +117,9 @@ + + @@ -356,6 +358,12 @@ + + + + + + Windows @@ -587,6 +595,9 @@ {03a8e721-8c98-438d-a8eb-c67a9e88e956} + + {bb2435da-29e4-489f-a207-f9e9f86ef14b} + {52dec27a-a711-4587-9a97-4ae9bdb7a050} From f4b6e53bb2ee2062f70b23c8032572ed549430fc Mon Sep 17 00:00:00 2001 From: chreden <4263940+chreden@users.noreply.github.com> Date: Sun, 4 Jan 2026 23:48:50 +0000 Subject: [PATCH 2/4] Update after rebase --- trview.app/ApplicationCreate.cpp | 8 +-- trview.app/Windows/Models/IModelsWindow.h | 19 ------ .../Windows/Models/IModelsWindowManager.h | 17 ------ trview.app/Windows/Models/ModelsWindow.cpp | 38 ++++++++++-- trview.app/Windows/Models/ModelsWindow.h | 18 ++++-- .../Windows/Models/ModelsWindowManager.cpp | 59 ------------------- .../Windows/Models/ModelsWindowManager.h | 28 --------- trview.app/Windows/Windows.cpp | 5 ++ trview.app/trview.app.vcxproj | 2 + trview.app/trview.app.vcxproj.filters | 18 ++---- 10 files changed, 60 insertions(+), 152 deletions(-) delete mode 100644 trview.app/Windows/Models/IModelsWindow.h delete mode 100644 trview.app/Windows/Models/IModelsWindowManager.h delete mode 100644 trview.app/Windows/Models/ModelsWindowManager.cpp delete mode 100644 trview.app/Windows/Models/ModelsWindowManager.h diff --git a/trview.app/ApplicationCreate.cpp b/trview.app/ApplicationCreate.cpp index db4eb4390..142101671 100644 --- a/trview.app/ApplicationCreate.cpp +++ b/trview.app/ApplicationCreate.cpp @@ -498,9 +498,12 @@ namespace trview pack_window->initialise(); return pack_window; }; + + auto transparency_buffer_source = [=](auto&& lts) { return std::make_unique(device, lts); }; + auto models_window_source = [=]() { - auto models_window = std::make_shared(files, dialogs, messaging); + auto models_window = std::make_shared(device, render_target_source, shader_storage, buffer_source, transparency_buffer_source, sampler_source, std::make_unique(window, std::make_unique(window)), messaging); models_window->initialise(); return models_window; }; @@ -558,9 +561,6 @@ namespace trview auto file_menu = std::make_shared(window, shortcuts, dialogs, files, level_name_source, messaging); messaging->add_recipient(file_menu); - auto transparency_buffer_source = [=](auto&& lts) { return std::make_unique(device, lts); }; - auto meshes_window_source = [=]() { return std::make_shared(device, render_target_source, shader_storage, buffer_source, transparency_buffer_source, sampler_source, std::make_unique(window, std::make_unique(window))); }; - auto application = std::make_shared( window, std::make_unique(window), diff --git a/trview.app/Windows/Models/IModelsWindow.h b/trview.app/Windows/Models/IModelsWindow.h deleted file mode 100644 index 4426180ee..000000000 --- a/trview.app/Windows/Models/IModelsWindow.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -namespace trview -{ - struct ILevelTextureStorage; - struct IModelStorage; - struct IModelsWindow - { - using Source = std::function()>; - virtual ~IModelsWindow() = 0; - virtual void render() = 0; - virtual void set_level_texture_storage(const std::weak_ptr& level_texture_storage) = 0; - virtual void set_model_storage(const std::weak_ptr& model_storage) = 0; - virtual void set_number(int32_t number) = 0; - virtual void update(float delta) = 0; - - Event<> on_window_closed; - }; -} diff --git a/trview.app/Windows/Models/IModelsWindowManager.h b/trview.app/Windows/Models/IModelsWindowManager.h deleted file mode 100644 index 3b55486ae..000000000 --- a/trview.app/Windows/Models/IModelsWindowManager.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -namespace trview -{ - struct ILevelTextureStorage; - struct IModelStorage; - struct IModelsWindow; - struct IModelsWindowManager - { - virtual ~IModelsWindowManager() = 0; - virtual std::weak_ptr create_window() = 0; - virtual void render() = 0; - virtual void set_level_texture_storage(const std::weak_ptr& level_texture_storage) = 0; - virtual void set_model_storage(const std::weak_ptr& model_storage) = 0; - virtual void update(float delta) = 0; - }; -} diff --git a/trview.app/Windows/Models/ModelsWindow.cpp b/trview.app/Windows/Models/ModelsWindow.cpp index e080d5d27..2d56af9b2 100644 --- a/trview.app/Windows/Models/ModelsWindow.cpp +++ b/trview.app/Windows/Models/ModelsWindow.cpp @@ -1,6 +1,8 @@ #include "ModelsWindow.h" #include "../../Geometry/Model/IModelStorage.h" #include "../../Geometry/Model/IModel.h" +#include "../../Messages/Messages.h" +#include "../../Elements/ILevel.h" #include #include @@ -20,19 +22,16 @@ namespace trview #pragma warning(pop) } - IModelsWindow::~IModelsWindow() - { - } - ModelsWindow::ModelsWindow(const std::shared_ptr& device, const graphics::IRenderTarget::SizeSource& render_target_source, const std::shared_ptr& shader_storage, const graphics::IBuffer::ConstantSource& buffer_source, ITransparencyBuffer::Source transparency_buffer_source, const graphics::ISamplerState::Source& sampler_source, - std::unique_ptr mouse) + std::unique_ptr mouse, + const std::weak_ptr& messaging) : _device(device), _render_target(render_target_source(512, 512, graphics::IRenderTarget::DepthStencilMode::Enabled)), _transparency_buffer_source(transparency_buffer_source), - _sampler_state(sampler_source(graphics::ISamplerState::AddressMode::Clamp)), _mouse(std::move(mouse)) + _sampler_state(sampler_source(graphics::ISamplerState::AddressMode::Clamp)), _mouse(std::move(mouse)), _messaging(messaging) { _vertex_shader = shader_storage->get("level_vertex_shader"); _pixel_shader = shader_storage->get("level_pixel_shader"); @@ -192,4 +191,31 @@ namespace trview ImGui::PopStyleVar(); return stay_open; } + + void ModelsWindow::initialise() + { + messages::get_open_level(_messaging, weak_from_this()); + } + + void ModelsWindow::receive_message(const Message& message) + { + if (auto level = messages::read_open_level(message)) + { + if (auto level_ptr = level->lock()) + { + set_level_texture_storage(level_ptr->texture_storage()); + set_model_storage(level_ptr->model_storage()); + } + } + } + + std::string ModelsWindow::title() const + { + return _id; + } + + std::string ModelsWindow::type() const + { + return "Models"; + } } diff --git a/trview.app/Windows/Models/ModelsWindow.h b/trview.app/Windows/Models/ModelsWindow.h index b9d7564a1..0da9e4e7e 100644 --- a/trview.app/Windows/Models/ModelsWindow.h +++ b/trview.app/Windows/Models/ModelsWindow.h @@ -1,17 +1,19 @@ #pragma once -#include "IModelsWindow.h" +#include "../IWindow.h" #include #include #include "../../Geometry/ITransparencyBuffer.h" #include #include #include "../../Camera/CameraInput.h" +#include +#include "../../Geometry/Model/IModelStorage.h" namespace trview { struct IModel; - class ModelsWindow final : public IModelsWindow + class ModelsWindow final : public IWindow, public std::enable_shared_from_this { public: ModelsWindow(const std::shared_ptr& device, @@ -20,15 +22,20 @@ namespace trview const graphics::IBuffer::ConstantSource& buffer_source, ITransparencyBuffer::Source transparency_buffer_source, const graphics::ISamplerState::Source& sampler_source, - std::unique_ptr mouse); + std::unique_ptr mouse, + const std::weak_ptr& messaging); virtual ~ModelsWindow() = default; void render() override; - void set_level_texture_storage(const std::weak_ptr& level_texture_storage) override; - void set_model_storage(const std::weak_ptr& model_storage) override; + void initialise(); void set_number(int32_t number) override; void update(float delta) override; + void receive_message(const Message& message) override; + std::string title() const override; + std::string type() const override; private: bool render_meshes_window(); + void set_level_texture_storage(const std::weak_ptr& level_texture_storage); + void set_model_storage(const std::weak_ptr& model_storage); std::string _id{ "Models 0" }; std::weak_ptr _model_storage; @@ -48,5 +55,6 @@ namespace trview TokenStore _token_store; CameraInput _camera_input; bool _mouse_over{ false }; + std::weak_ptr _messaging; }; } diff --git a/trview.app/Windows/Models/ModelsWindowManager.cpp b/trview.app/Windows/Models/ModelsWindowManager.cpp deleted file mode 100644 index 0f267caa6..000000000 --- a/trview.app/Windows/Models/ModelsWindowManager.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "ModelsWindowManager.h" -#include "../../Resources/resource.h" - -namespace trview -{ - IModelsWindowManager::~IModelsWindowManager() - { - } - - ModelsWindowManager::ModelsWindowManager(const Window& window, const IModelsWindow::Source& models_window_source) - : MessageHandler(window), _models_window_source(models_window_source) - { - } - - std::weak_ptr ModelsWindowManager::create_window() - { - auto models_window = _models_window_source(); - models_window->set_level_texture_storage(_level_texture_storage); - models_window->set_model_storage(_model_storage); - return add_window(models_window); - } - - std::optional ModelsWindowManager::process_message(UINT message, WPARAM wParam, LPARAM) - { - if (message == WM_COMMAND && LOWORD(wParam) == ID_WINDOWS_MODELS) - { - create_window(); - } - return {}; - } - - void ModelsWindowManager::render() - { - WindowManager::render(); - } - - void ModelsWindowManager::set_level_texture_storage(const std::weak_ptr& level_texture_storage) - { - _level_texture_storage = level_texture_storage; - for (auto& window : _windows) - { - window.second->set_level_texture_storage(_level_texture_storage); - } - } - - void ModelsWindowManager::set_model_storage(const std::weak_ptr& model_storage) - { - _model_storage = model_storage; - for (auto& window : _windows) - { - window.second->set_model_storage(_model_storage); - } - } - - void ModelsWindowManager::update(float delta) - { - WindowManager::update(delta); - } -} diff --git a/trview.app/Windows/Models/ModelsWindowManager.h b/trview.app/Windows/Models/ModelsWindowManager.h deleted file mode 100644 index 14453b135..000000000 --- a/trview.app/Windows/Models/ModelsWindowManager.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include - -#include "../WindowManager.h" -#include "IModelsWindowManager.h" -#include "IModelsWindow.h" - -namespace trview -{ - struct ILevelTextureStorage; - class ModelsWindowManager final : public IModelsWindowManager, public WindowManager, public MessageHandler - { - public: - explicit ModelsWindowManager(const Window& window, const IModelsWindow::Source& models_window_source); - virtual ~ModelsWindowManager() = default; - virtual std::weak_ptr create_window() override; - std::optional process_message(UINT message, WPARAM wParam, LPARAM lParam) override; - void render() override; - void set_level_texture_storage(const std::weak_ptr& level_texture_storage) override; - void set_model_storage(const std::weak_ptr& model_storage) override; - void update(float delta) override; - private: - IModelsWindow::Source _models_window_source; - std::weak_ptr _level_texture_storage; - std::weak_ptr _model_storage; - }; -} diff --git a/trview.app/Windows/Windows.cpp b/trview.app/Windows/Windows.cpp index 5113f012f..685af627e 100644 --- a/trview.app/Windows/Windows.cpp +++ b/trview.app/Windows/Windows.cpp @@ -72,6 +72,11 @@ namespace trview create("Log"); break; } + case ID_WINDOWS_MODELS: + { + create("Models"); + break; + } case ID_WINDOWS_PACK: { create("Pack"); diff --git a/trview.app/trview.app.vcxproj b/trview.app/trview.app.vcxproj index 48224e360..f1d9a8878 100644 --- a/trview.app/trview.app.vcxproj +++ b/trview.app/trview.app.vcxproj @@ -88,6 +88,7 @@ copy ""$(OutDir)*.cso"" ""$(ProjectDir)Resources\Generated"" + @@ -223,6 +224,7 @@ copy ""$(OutDir)*.cso"" ""$(ProjectDir)Resources\Generated"" + diff --git a/trview.app/trview.app.vcxproj.filters b/trview.app/trview.app.vcxproj.filters index 48c85a065..dad6da89f 100644 --- a/trview.app/trview.app.vcxproj.filters +++ b/trview.app/trview.app.vcxproj.filters @@ -117,9 +117,8 @@ - - + @@ -358,19 +357,10 @@ - - - - - - - - Windows - - - Mocks\Windows - + + + From 7adc7517887187922bd1e39df546d6c9a3c091dc Mon Sep 17 00:00:00 2001 From: chreden <4263940+chreden@users.noreply.github.com> Date: Sun, 4 Jan 2026 23:51:40 +0000 Subject: [PATCH 3/4] Update ApplicationCreate.cpp --- trview.app/ApplicationCreate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/trview.app/ApplicationCreate.cpp b/trview.app/ApplicationCreate.cpp index 142101671..307809aa9 100644 --- a/trview.app/ApplicationCreate.cpp +++ b/trview.app/ApplicationCreate.cpp @@ -516,6 +516,7 @@ namespace trview windows->register_window("Items", items_window_source); windows->register_window("Lights", lights_window_source); windows->register_window("Log", log_window_source); + windows->register_window("Models", models_window_source); windows->register_window("Pack", pack_window_source); windows->register_window("Plugins", plugins_window_source); windows->register_window("Rooms", rooms_window_source); @@ -524,7 +525,6 @@ namespace trview windows->register_window("Statics", statics_window_source); windows->register_window("Textures", textures_window_source); windows->register_window("Triggers", triggers_window_source); - windows->register_window("Models", models_window_source); auto viewer_ui = std::make_shared( window, From 2970d72a1d32aae7c0b75f906e2b8771683e2557 Mon Sep 17 00:00:00 2001 From: chreden <4263940+chreden@users.noreply.github.com> Date: Sun, 4 Jan 2026 23:52:28 +0000 Subject: [PATCH 4/4] Remove mocks --- trview.app/Mocks/Windows/IModelsWindow.h | 19 ------------------ .../Mocks/Windows/IModelsWindowManager.h | 20 ------------------- 2 files changed, 39 deletions(-) delete mode 100644 trview.app/Mocks/Windows/IModelsWindow.h delete mode 100644 trview.app/Mocks/Windows/IModelsWindowManager.h diff --git a/trview.app/Mocks/Windows/IModelsWindow.h b/trview.app/Mocks/Windows/IModelsWindow.h deleted file mode 100644 index 35bf8cbd8..000000000 --- a/trview.app/Mocks/Windows/IModelsWindow.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -#include "../../Windows/Models/IModelsWindow.h" - -namespace trview -{ - namespace mocks - { - struct MockModelsWindow : public IModelsWindow - { - MockModelsWindow(); - virtual ~MockModelsWindow(); - MOCK_METHOD(void, render, (), (override)); - MOCK_METHOD(void, set_model_storage, (const std::weak_ptr&), (override)); - MOCK_METHOD(void, update, (float), (override)); - MOCK_METHOD(void, set_number, (int32_t), (override)); - }; - } -} diff --git a/trview.app/Mocks/Windows/IModelsWindowManager.h b/trview.app/Mocks/Windows/IModelsWindowManager.h deleted file mode 100644 index 1fc5f41b1..000000000 --- a/trview.app/Mocks/Windows/IModelsWindowManager.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "../../Windows/Models/IModelsWindowManager.h" - -namespace trview -{ - namespace mocks - { - struct MockModelsWindowManager : public IModelsWindowManager - { - MockModelsWindowManager(); - virtual ~MockModelsWindowManager(); - MOCK_METHOD(std::weak_ptr, create_window, (), (override)); - MOCK_METHOD(void, render, (), (override)); - MOCK_METHOD(void, set_level_texture_storage, (const std::weak_ptr&), (override)); - MOCK_METHOD(void, set_model_storage, (const std::weak_ptr&), (override)); - MOCK_METHOD(void, update, (float), (override)); - }; - } -}