Files
FluidSimCpp/include/FluidSim/DesktopScenario.hpp
max bf8621ad39 Load engines from TOML and improve scenario audio and carb tuning
Migrate 32 engine definitions into validated startup-loaded TOML files and separate configuration and snapshots from scenario assembly. Add camera-based stereo propagation from shared pipe geometry, preserve exhaust character, tune carburetor mixtures and idle, use ignition cuts for carb engines, and support returning to the scenario menu.

Validation: full Windows build and all 14 CTest checks passed.
2026-10-24 03:24:35 +02:00

97 lines
4.4 KiB
C++

#pragma once
#include "FluidSim/Scenario.hpp"
#include "FluidSim/ScenarioCatalog.hpp"
#include "FluidSim/TwoStrokeScenario.hpp"
#include <memory>
#include <mutex>
namespace fluidsim::port {
// Shared desktop controls and snapshots. Each simulation owns its state lock.
class DesktopScenario {
public:
explicit DesktopScenario(const ScenarioExample& e)
: DesktopScenario(e.kind,e.two,e.exhaust,e.junctions,e.engine,e.title) {}
explicit DesktopScenario(ScenarioKind kind, TwoStrokeConfig config = {}, ExhaustConfig exhaust = {}, JunctionConfig junctions = {}, EngineConfig engine = {}, std::string name = {}) : name_(std::move(name)) {
if (kind == ScenarioKind::TwoStroke)
two_ = std::make_unique<TwoStrokeScenario>(44100, config);
else {
if(!engine.cylinders)engine=preset_engine_definition(kind==ScenarioKind::SaabB205?
FourStrokePreset::SaabB205:kind==ScenarioKind::AmericanV8?FourStrokePreset::AmericanV8:FourStrokePreset::Twin).engine;
four_ = std::make_unique<FourStrokeScenario>(44100,std::move(engine),exhaust,junctions);
}
}
AudioFrame process() {
if (four_)
return four_->process();
const std::lock_guard lock(mutex_);
return two_->process();
}
void set_controls(float pedal, float load, float clutch) {
visit([&](auto& scenario) { scenario.set_controls(pedal, load, clutch); });
}
void set_starter(bool engaged) { visit([&](auto& scenario) { scenario.set_starter(engaged); }); }
void shift_up() { visit([](auto& scenario) { scenario.shift_up(); }); }
void shift_down() { visit([](auto& scenario) { scenario.shift_down(); }); }
void set_neutral() { visit([](auto& scenario) { scenario.set_neutral(); }); }
void set_pipe_solver(PipeSolver solver) { visit([&](auto& scenario) { scenario.set_pipe_solver(solver); }); }
void set_audio_scene(OutdoorScene scene) {
visit([&](auto& scenario) { scenario.set_audio_scene(scene); });
}
void set_listener(AudioListener listener) {
visit([&](auto& scenario) { scenario.set_listener(listener); });
}
ScenarioSnapshot snapshot() const {
if (four_) { auto s=four_->snapshot();s.example_name=name_;return s; }
const std::lock_guard lock(mutex_);
const auto source = two_->snapshot();
ScenarioSnapshot s;
s.example_name=name_;
s.pipe_solver=two_->pipes.front().solver;
s.two_stroke = true;
s.cylinder_angles[0]=two_->crankshaft.angle;
s.cylinder_count = 1;
s.racing_exhaust = two_->configuration.exhaust == TwoStrokeExhaust::Racing;
s.rpm = source.rpm;
s.speed_kmh = two_->vehicle.speed * 3.6f;
s.kinetic_energy = two_->vehicle.kinetic_energy();
s.gear = two_->vehicle.current_gear;
s.clutch = two_->clutch;
s.average_power = two_->crankshaft.average_power;
s.average_torque = two_->crankshaft.average_torque;
s.cylinder_pressure1 = source.cylinder_pressure;
s.cylinder_temperature1 = two_->cylinder.temperature();
s.cylinder_piston_fraction1 =
(two_->cylinder.volume - two_->cylinder.clearance_volume()) / two_->cylinder.swept_volume();
s.crankcase_pressure = source.crankcase_pressure;
s.intake_pressure = source.intake_pressure;
s.exhaust_pressure = two_->pipes[3].state(0).pressure;
s.throttle = source.throttle;
s.pedal = source.pedal;
s.target_afr = two_->carburetor.indicated_afr();
s.valve1_intake_area = source.transfer_area;
s.valve1_exhaust_area = source.exhaust_area;
s.reed_open = source.reed_open;
s.fuel_cut = source.fuel_cut;
s.ignition_cut = source.ignition_cut;
s.idle_control = two_->ecu.idle_active();
s.rev_limit1 = two_->ecu.rev_limited();
s.audio_scene = source.audio_scene;
s.intake_flow = two_->intake_flow;
s.exhaust_flow = two_->exhaust_flow;
s.capture_pipes(two_->pipes);
return s;
}
private:
template<class Function> void visit(Function&& function) {
const std::lock_guard lock(mutex_);
if (four_) function(*four_);
else function(*two_);
}
std::string name_;
std::unique_ptr<FourStrokeScenario> four_;
std::unique_ptr<TwoStrokeScenario> two_;
mutable std::mutex mutex_;
};
} // namespace fluidsim::port