// 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"; std::cerr << "Example: " << argv[0] << " assets/demo.seq src/timeline.cc\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; }