L'adaptateur traduit clavier et manette en actions SEQUA (PlayerInput) ; le Core ne recoit et n'enregistre que ces actions, le rejeu reste exact. - SEQUA 3D : PlayerInput, poll_input(), capacite player_input (interface des adaptateurs en version 2) - protocole Core - rendu 0.2 : render/input, envoye quand les actions changent ; entree remise au neutre si le rendu disparait - Godot : InputMap du projet (ZQSD, fleches, espace, manette) ou script SequaInput.sequa_player_input() pour une correspondance avancee ; entree neutre sans focus ; exemple examples/sequa_input.gd - adaptateur debug : entrees scriptees par numero d'image - Studio : ligne "Entrees du joueur" du panneau Rendu
125 lines
4.2 KiB
C++
125 lines
4.2 KiB
C++
// SPDX-License-Identifier: MPL-2.0
|
|
#include "sequa/debug/debug_adapter.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <chrono>
|
|
#include <utility>
|
|
|
|
namespace sequa::debug {
|
|
|
|
using three_d::BufferSet;
|
|
using three_d::Error;
|
|
using three_d::FrameBuffer;
|
|
|
|
DebugEngineAdapter::DebugEngineAdapter(std::unique_ptr<FrameSink> sink, ViewParams view,
|
|
std::vector<ScriptedInput> script)
|
|
: sink_(std::move(sink)), view_(view), script_(std::move(script))
|
|
{
|
|
std::stable_sort(script_.begin(), script_.end(),
|
|
[](const ScriptedInput& a, const ScriptedInput& b) { return a.frame < b.frame; });
|
|
}
|
|
|
|
three_d::EngineCapabilities DebugEngineAdapter::capabilities() const
|
|
{
|
|
three_d::EngineCapabilities capabilities;
|
|
capabilities.engine_name = "debug";
|
|
capabilities.engine_version = "0.1";
|
|
// Seul le tampon couleur existe : la vue de côté ne calcule pas la profondeur.
|
|
capabilities.buffers = BufferSet{FrameBuffer::color};
|
|
capabilities.windowed = false;
|
|
capabilities.headless = true;
|
|
// max_width/max_height laissés à 0 : aucune limite de taille.
|
|
capabilities.player_input = !script_.empty();
|
|
return capabilities;
|
|
}
|
|
|
|
std::optional<three_d::PlayerInput> DebugEngineAdapter::poll_input()
|
|
{
|
|
if (!running_ || script_.empty()) {
|
|
return std::nullopt;
|
|
}
|
|
three_d::PlayerInput current;
|
|
if (last_frame_) {
|
|
for (const ScriptedInput& entry : script_) {
|
|
if (entry.frame > *last_frame_) {
|
|
break;
|
|
}
|
|
current = entry.input;
|
|
}
|
|
}
|
|
return current;
|
|
}
|
|
|
|
std::optional<Error> DebugEngineAdapter::start(const three_d::EngineConfig& config)
|
|
{
|
|
if (running_) {
|
|
return Error{Error::Code::already_started, "adaptateur de débogage déjà démarré"};
|
|
}
|
|
if (!config.headless) {
|
|
return Error{Error::Code::unsupported,
|
|
"l'adaptateur de débogage ne sait faire que du rendu hors écran"};
|
|
}
|
|
if (config.width == 0 || config.height == 0) {
|
|
return Error{Error::Code::invalid_argument, "taille d'image nulle"};
|
|
}
|
|
// Le rendu « fenêtre » n'existe pas : la taille du start() est celle des images.
|
|
width_ = config.width;
|
|
height_ = config.height;
|
|
scene_.clear(); // après start(), la scène du moteur est vide (l'appelant pousse un snapshot)
|
|
last_frame_.reset();
|
|
running_ = true;
|
|
return std::nullopt;
|
|
}
|
|
|
|
void DebugEngineAdapter::stop() noexcept
|
|
{
|
|
running_ = false;
|
|
}
|
|
|
|
std::optional<Error> DebugEngineAdapter::apply(const three_d::SceneDelta& delta)
|
|
{
|
|
if (!running_) {
|
|
return Error{Error::Code::not_started, "adaptateur de débogage arrêté"};
|
|
}
|
|
return scene_.apply(delta);
|
|
}
|
|
|
|
three_d::FrameOutcome DebugEngineAdapter::render_frame(const three_d::FrameRequest& request)
|
|
{
|
|
if (!running_) {
|
|
return Error{Error::Code::not_started, "adaptateur de débogage arrêté"};
|
|
}
|
|
// Taille effective : celle de la requête si elle est non nulle, sinon celle de start().
|
|
three_d::FrameRequest effective = request;
|
|
if (effective.width == 0) {
|
|
effective.width = width_;
|
|
}
|
|
if (effective.height == 0) {
|
|
effective.height = height_;
|
|
}
|
|
if (std::optional<Error> error = three_d::validate_request(effective, capabilities())) {
|
|
return *std::move(error);
|
|
}
|
|
|
|
const auto begin = std::chrono::steady_clock::now();
|
|
last_image_ = draw_side_view(scene_, effective.width, effective.height, view_);
|
|
if (sink_ != nullptr) {
|
|
if (std::optional<Error> error = sink_->write(last_image_)) {
|
|
// Le dessin a réussi mais l'écriture a échoué : erreur du sink renvoyée telle
|
|
// quelle ; PROVISOIRE, l'image reste consultable via last_image().
|
|
return *std::move(error);
|
|
}
|
|
}
|
|
last_frame_ = request.frame_index;
|
|
three_d::FrameResult result;
|
|
result.frame_index = request.frame_index;
|
|
result.world_version = scene_.world_version();
|
|
result.produced = BufferSet{FrameBuffer::color};
|
|
result.measurements.cpu_ms = std::chrono::duration<double, std::milli>(
|
|
std::chrono::steady_clock::now() - begin)
|
|
.count();
|
|
return result;
|
|
}
|
|
|
|
} // namespace sequa::debug
|