#include "Gallery_Scene3D.h" #include "../Gallery_Capture_Json.h" #include "../common/Observer_Json.h" #include "../common/Pixel_Frame.h" #include "render_3D/Render_Scene_3D.h" #include #include #include #include #include namespace renderive::web { template <> struct Observer_Pfr_Adapter { using Scene = renderive::render_3d::Render_Scene_3D; using Type = adminive::Boost_Pfr_Base_Reflection_Adapter< Scene::State, Scene::State_Fields>; }; template <> struct Observer_Pfr_Adapter { using Scene = renderive::render_3d::Render_Scene_3D; using Type = adminive::Boost_Pfr_Base_Reflection_Adapter< Scene::Observer, Scene::Observer_Fields>; }; namespace { using namespace renderive::render_3d; std::shared_ptr make_strategy( Gallery_Frame_Mode mode) { switch (mode) { case Gallery_Frame_Mode::Manual: return std::make_shared(); case Gallery_Frame_Mode::Low_Latency: return std::make_shared(); case Gallery_Frame_Mode::Playback: return std::make_shared(); } return {}; } std::string_view mode_id(Gallery_Frame_Mode mode) { switch (mode) { case Gallery_Frame_Mode::Manual: return "manual"; case Gallery_Frame_Mode::Low_Latency: return "low_latency"; case Gallery_Frame_Mode::Playback: return "playback"; } return "low_latency"; } Visual_Family visual_family(std::string_view case_id) { if (case_id == "datoviz_splat") return Visual_Family::Splat; if (case_id == "datoviz_pixel") return Visual_Family::Pixel; if (case_id == "datoviz_marker") return Visual_Family::Marker; if (case_id == "datoviz_sphere") return Visual_Family::Sphere; if (case_id == "datoviz_segment") return Visual_Family::Segment; if (case_id == "datoviz_vector") return Visual_Family::Vector; if (case_id == "datoviz_primitive") return Visual_Family::Primitive; if (case_id == "datoviz_mesh") return Visual_Family::Mesh; if (case_id == "datoviz_path") return Visual_Family::Path; if (case_id == "datoviz_image") return Visual_Family::Image; if (case_id == "datoviz_labels") return Visual_Family::Labels; if (case_id == "datoviz_glyph") return Visual_Family::Glyph; if (case_id == "datoviz_text") return Visual_Family::Text; if (case_id == "datoviz_volume") return Visual_Family::Volume; return Visual_Family::Point; } class Gallery_Scene3D final : public Gallery_Scene_Interface { public: Gallery_Scene3D(std::string case_id, Gallery_Frame_Mode mode, bool automatic) : case_id_(std::move(case_id)), mode_(mode), automatic_(automatic) { rebuild(); } const std::string& case_id() const noexcept override { return case_id_; } nlohmann::json controls() const override { return {{"resources", nlohmann::json::array()}, {"observers", nlohmann::json::array()}, {"render_plan", gallery_render_plan_json(*scene_)}, {"performance_capture", gallery_performance_capture_json(*scene_)}}; } Gallery_Frame_Mode frame_mode() const noexcept override { return mode_; } void reset_monitoring() override { render_count_ = 0; } bool can_render_automatically() const noexcept override { return automatic_ && mode_ == Gallery_Frame_Mode::Low_Latency; } std::uint64_t kernel_refresh_interval_ns() const override { return scene_->observer().next_refresh_interval_ns; } void set_client_metrics(Gallery_Client_Performance metrics) noexcept override { client_ = metrics; } adminive::Update_Result apply_patch( std::string_view, const nlohmann::json&) override { return {false, "no editable resource matches the requested target"}; } void resize(int width, int height) override { const Extent requested{static_cast(std::max(1, width)), static_cast(std::max(1, height))}; if (requested.width == extent_.width && requested.height == extent_.height) return; extent_ = requested; rebuild(); } void dispatch(const Gallery_Input_Event&) override {} Gallery_Render_Result render_latest_frame() override { if (scene_->request_frame() != Frame_Request_Result::none) return Gallery_Render_Result::backend_failure; ++render_count_; return Gallery_Render_Result::none; } std::optional encode_latest_pixels() override { const auto frame = scene_->latest_frame(); if (!frame || frame->rgba8.empty()) return std::nullopt; auto pixels = encode_rgba8_pixel_frame( frame->rgba8.data(), frame->extent.width, frame->extent.height, frame->extent.width * 4U, frame->sequence); return pixels.empty() ? std::nullopt : std::optional(std::move(pixels)); } void record_pixel_response(std::chrono::steady_clock::time_point, std::chrono::steady_clock::time_point, std::chrono::steady_clock::time_point, std::size_t) override {} std::string action(const Gallery_Action_Request& request, bool& recognized) override { recognized = true; if (request.id == "frame_strategy_manual" || request.id == "frame_strategy_low_latency" || request.id == "frame_strategy_playback") { const auto next = request.id == "frame_strategy_manual" ? Gallery_Frame_Mode::Manual : request.id == "frame_strategy_playback" ? Gallery_Frame_Mode::Playback : Gallery_Frame_Mode::Low_Latency; const auto operation = scene_->edit_renderables( [strategy = make_strategy(next)]( Scene_3D_Base::Renderable_Editor& editor) mutable { editor.replace_frame_control_strategy(std::move(strategy)); }); if (operation.wait() != Scene_Edit_Error::none) return "frame strategy replacement rejected"; mode_ = next; return "frame strategy replaced"; } if (request.id == "mode_cycle" || request.id == "mode_render") { static_cast(render_latest_frame()); return "frame rendered"; } if (request.id == "capture_next_frame") { const auto capture = scene_->capture_next_frame(); if (capture) static_cast(render_latest_frame()); return capture ? "frame captured" : "capture request rejected"; } if (request.id == "capture_frames") { std::size_t count = 20; if (request.argument && std::holds_alternative(*request.argument)) count = static_cast(std::clamp( std::get(*request.argument), 1.0, 1000.0)); const auto capture = scene_->capture_frames(count); if (capture) static_cast(render_latest_frame()); return capture ? "frame capture started" : "capture request rejected"; } recognized = false; return {}; } void record_action_notice_if_empty(std::string_view) override {} std::string telemetry_json() const override { const auto observer = scene_->observer(); auto kernel_observer = observer_json(observer); kernel_observer["mode"] = mode_id(mode_); kernel_observer["event"] = "render_completed"; kernel_observer["limit"] = "none"; return nlohmann::json{ {"kernel_observer", std::move(kernel_observer)}, {"performance", {{"successful_render_count", render_count_}}}, {"client_performance", {{"transport_fps", client_.transport_fps}}}, {"performance_capture", gallery_performance_capture_json(*scene_)}} .dump(); } private: void rebuild() { std::vector points{ {{-0.55F, -0.25F, 0.0F}, {255, 80, 80, 255}, 24.0F}, {{0.0F, 0.45F, 0.0F}, {80, 255, 120, 255}, 28.0F}, {{0.55F, -0.1F, 0.0F}, {80, 140, 255, 255}, 26.0F}}; visual_ = Point_Visual::Builder{}.build(std::move(points)); visual_->set_object_name(case_id_ + " Points"); Render_Scene_3D::State state; state.viewport = extent_; state.visual_family = visual_family(case_id_); scene_ = Render_Scene_3D::Builder{state}.build( visual_, make_strategy(mode_)); static_cast(scene_->request_frame()); } std::string case_id_; Gallery_Frame_Mode mode_; bool automatic_{}; Extent extent_{560, 320}; std::shared_ptr visual_; std::unique_ptr scene_; Gallery_Client_Performance client_; std::uint64_t render_count_{}; }; } // namespace std::unique_ptr make_gallery_scene_3d( std::uint64_t, std::string case_id, Gallery_Frame_Mode mode, bool automatic_low_latency) { return std::make_unique( std::move(case_id), mode, automatic_low_latency); } } // namespace renderive::web