Improve physical geometry, fluid solvers, audio, and profiling

This commit is contained in:
max
2026-10-30 18:57:56 +01:00
parent bf8621ad39
commit eb2b6366c2
130 changed files with 14649 additions and 862 deletions

View File

@@ -11,7 +11,7 @@ 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)) {
explicit DesktopScenario(ScenarioKind kind, TwoStrokeConfig config = {}, ExhaustConfig exhaust = {}, JunctionConfig junctions = {}, EngineConfig engine = {}, std::string name = {}) : name_(std::move(name)),exhaust_(exhaust),junctions_(junctions) {
if (kind == ScenarioKind::TwoStroke)
two_ = std::make_unique<TwoStrokeScenario>(44100, config);
else {
@@ -21,33 +21,74 @@ public:
}
}
AudioFrame process() {
const std::lock_guard lock(mutex_);
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); });
visit([&](auto& scenario) { pedal_=pedal;load_=load;clutch_=clutch;scenario.set_controls(pedal, load, clutch); });
}
void set_starter(bool engaged) { visit([&](auto& scenario) { scenario.set_starter(engaged); }); }
void set_starter(bool engaged) { visit([&](auto& scenario) { starter_=engaged;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_pipe_solver(PipeSolver solver) { visit([&](auto& scenario) { solver_=solver;scenario.set_pipe_solver(solver); }); }
void set_audio_scene(OutdoorScene scene) {
visit([&](auto& scenario) { scenario.set_audio_scene(scene); });
visit([&](auto& scenario) { scene_=scene;scenario.set_audio_scene(scene); });
}
void set_listener(AudioListener listener) {
visit([&](auto& scenario) { scenario.set_listener(listener); });
visit([&](auto& scenario) { listener_=listener;scenario.set_listener(listener); });
}
void set_tuning(const EngineConfig& tuning) {
const std::lock_guard lock(mutex_);
if(four_)four_->set_tuning(tuning);
}
// Build on a background thread. Publishing takes the same lock as process(),
// so no solver/port pointers can be observed during the swap.
void rebuild_engine(const EngineConfig& engine) {
validate_engine_config(engine,"pipe rebuild");
auto replacement=std::make_unique<FourStrokeScenario>(44100,engine,exhaust_,junctions_);
{
const std::lock_guard lock(mutex_);
if(!four_)throw std::invalid_argument("Live editor requires a four-stroke engine");
replacement->set_controls(pedal_,load_,clutch_);
replacement->set_starter(starter_);replacement->set_pipe_solver(solver_);
replacement->set_audio_scene(scene_);replacement->set_listener(listener_);
four_.swap(replacement);
}
}
void set_cells_per_meter(float density) {
mesh_cell_count(.1f,density);
EngineConfig engine;TwoStrokeConfig two_config;bool is_two;
{
const std::lock_guard lock(mutex_);
is_two=bool(two_);
if(is_two) two_config=two_->configuration; else engine=four_->engine_config;
}
std::unique_ptr<FourStrokeScenario> next_four;
std::unique_ptr<TwoStrokeScenario> next_two;
if(is_two) { two_config.cells_per_meter=density;two_config.exhaust_cell_length=1/density;next_two=std::make_unique<TwoStrokeScenario>(44100,two_config); }
else { engine.cells_per_meter=density;next_four=std::make_unique<FourStrokeScenario>(44100,engine,exhaust_,junctions_); }
{
const std::lock_guard lock(mutex_);
const auto restore=[&](auto& scenario) {
scenario.set_controls(pedal_,load_,clutch_);scenario.set_starter(starter_);
scenario.set_pipe_solver(solver_);scenario.set_audio_scene(scene_);scenario.set_listener(listener_);
};
if(is_two) {restore(*next_two);two_.swap(next_two);}
else {restore(*next_four);four_.swap(next_four);}
}
}
ScenarioSnapshot snapshot() const {
if (four_) { auto s=four_->snapshot();s.example_name=name_;return s; }
const std::lock_guard lock(mutex_);
if (four_) { auto s=four_->snapshot();s.example_name=name_;s.cells_per_meter=four_->engine_config.cells_per_meter;return s; }
const auto source = two_->snapshot();
ScenarioSnapshot s;
s.example_name=name_;
s.pipe_solver=two_->pipes.front().solver;
s.two_stroke = true;
s.cells_per_meter=two_->configuration.cells_per_meter;
s.cylinder_angles[0]=two_->crankshaft.angle;
s.cylinder_count = 1;
s.racing_exhaust = two_->configuration.exhaust == TwoStrokeExhaust::Racing;
@@ -79,6 +120,7 @@ public:
s.intake_flow = two_->intake_flow;
s.exhaust_flow = two_->exhaust_flow;
s.capture_pipes(two_->pipes);
two_->capture_geometry(s);
return s;
}
@@ -89,6 +131,13 @@ private:
else function(*two_);
}
std::string name_;
ExhaustConfig exhaust_;
JunctionConfig junctions_;
float pedal_{},load_{},clutch_{};
bool starter_{};
PipeSolver solver_{PipeSolver::Rusanov};
OutdoorScene scene_{OutdoorScene::Wall};
AudioListener listener_{};
std::unique_ptr<FourStrokeScenario> four_;
std::unique_ptr<TwoStrokeScenario> two_;
mutable std::mutex mutex_;