From 9a6cd87164995df93cf3df410ce37721910ce240 Mon Sep 17 00:00:00 2001 From: skal Date: Sat, 31 Jan 2026 15:57:00 +0100 Subject: feat: Implement Sequence Compiler for data-driven choreography Adds a 'seq_compiler' tool that converts a text-based timeline (assets/demo.seq) into a generated C++ file. This allows editing effect sequences and timing without modifying engine code. Replaces manual sequence creation with a generated 'LoadTimeline' function. --- tools/seq_compiler.cc | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 tools/seq_compiler.cc (limited to 'tools') diff --git a/tools/seq_compiler.cc b/tools/seq_compiler.cc new file mode 100644 index 0000000..8da4e4e --- /dev/null +++ b/tools/seq_compiler.cc @@ -0,0 +1,130 @@ +// This file is part of the 64k demo project. +// It implements the sequence compiler tool. +// Converts a text-based timeline description into C++ code. + +#include +#include +#include +#include +#include + +struct EffectEntry { + std::string class_name; + std::string start; + std::string end; + std::string priority; + std::string extra_args; +}; + +struct SequenceEntry { + std::string start_time; + std::string priority; + std::vector effects; +}; + +std::string trim(const std::string &str) { + size_t first = str.find_first_not_of(" "); + if (std::string::npos == first) + return str; + size_t last = str.find_last_not_of(" "); + return str.substr(first, (last - first + 1)); +} + +int main(int argc, char *argv[]) { + if (argc != 3) { + std::cerr << "Usage: " << argv[0] << " \n"; + return 1; + } + + std::ifstream in_file(argv[1]); + if (!in_file.is_open()) { + std::cerr << "Error: Could not open input file " << argv[1] << "\n"; + return 1; + } + + std::vector sequences; + SequenceEntry *current_seq = nullptr; + + std::string line; + int line_num = 0; + while (std::getline(in_file, line)) { + line_num++; + std::string trimmed = trim(line); + if (trimmed.empty() || trimmed[0] == '#') + continue; + + std::stringstream ss(trimmed); + std::string command; + ss >> command; + + if (command == "SEQUENCE") { + std::string start, priority; + if (!(ss >> start >> priority)) { + std::cerr << "Error line " << line_num + << ": SEQUENCE requires \n"; + return 1; + } + sequences.push_back({start, priority, {}}); + current_seq = &sequences.back(); + } else if (command == "EFFECT") { + if (!current_seq) { + std::cerr << "Error line " << line_num + << ": EFFECT found outside of SEQUENCE\n"; + return 1; + } + std::string class_name, start, end, priority; + if (!(ss >> class_name >> start >> end >> priority)) { + std::cerr << "Error line " << line_num + << ": EFFECT requires \n"; + return 1; + } + + // Capture remaining args + std::string extra_args; + std::getline(ss, extra_args); // Read rest of line + // Remove leading whitespace from getline if any (getline reads the space after priority) + extra_args = trim(extra_args); + if (!extra_args.empty()) { + extra_args = ", " + extra_args; + } + + current_seq->effects.push_back( + {class_name, start, end, priority, extra_args}); + } else { + std::cerr << "Error line " << line_num << ": Unknown command '" << command + << "'\n"; + return 1; + } + } + + std::ofstream out_file(argv[2]); + if (!out_file.is_open()) { + std::cerr << "Error: Could not open output file " << argv[2] << "\n"; + return 1; + } + + out_file << "// Auto-generated by seq_compiler. Do not edit.\n"; + out_file << "#include \"gpu/demo_effects.h\"\n"; + out_file << "#include \"gpu/effect.h\"\n\n"; + out_file << "void LoadTimeline(MainSequence& main_seq, WGPUDevice device, " + "WGPUQueue queue, WGPUTextureFormat format) {\n"; + + for (const auto &seq : sequences) { + out_file << " {\n"; + out_file << " auto seq = std::make_shared();\n"; + for (const auto &eff : seq.effects) { + out_file << " seq->add_effect(std::make_shared<" << eff.class_name + << ">(device, queue, format" << eff.extra_args << "), " + << eff.start << "f, " << eff.end << "f, " << eff.priority << ");\n"; + } + out_file << " main_seq.add_sequence(seq, " << seq.start_time << "f, " + << seq.priority << ");\n"; + out_file << " }\n"; + } + + out_file << "}\n"; + + std::cout << "Successfully generated timeline with " << sequences.size() << " sequences.\n"; + + return 0; +} -- cgit v1.2.3