#include #include #include #include #include #include "util/container.h" #include "util/file.h" #include "util/err.h" #include "scene.h" #include "parse.h" #include "render.h" #include "print.h" #include "draw/opengl.h" enum param_type { PARAM_OUT = 1 << 0, PARAM_SIZE = 1 << 1, PARAM_FPS = 1 << 2, PARAM_LIVE = 1 << 3, PARAM_VERBOSE = 1 << 4, }; struct params { unsigned int flags; str infile; str outfile; unsigned int size[2]; unsigned int fps; option(double) live; }; static void run(struct registry *reg, struct params *params) { FILE *srcfile; str srcpath; if (array_eq(params->infile, S("-"))) { srcfile = stdin; srcpath = S(""); } else { srcpath = params->infile; srcfile = file_open(params->infile, "r"); if (!srcfile) eprintf("'%.*s': failed to open scene file\n", PSTR(params->infile)); } str contents = file_read(srcfile); fclose(srcfile); struct source src; struct scene scene; source_init(&src, srcpath, contents); scene_init(&scene); parse_scene(&src, &scene, reg); if (params->flags & PARAM_VERBOSE) print_scene(stderr, &scene, SCENE_PARSED); scene_bind(&scene, &src, reg); registry_free(reg); if (params->flags & PARAM_VERBOSE) print_scene(stderr, &scene, SCENE_BOUND); if (params->flags & PARAM_OUT) scene.outfile = params->outfile; if (params->flags & PARAM_SIZE) { scene.size[0] = params->size[0]; scene.size[1] = params->size[1]; } if (params->flags & PARAM_FPS) scene.fps = params->fps; if (params->flags & PARAM_LIVE) { scene.live.present = true; scene.live.data = params->live.present ? params->live.data : 1.0; } if (params->flags & PARAM_VERBOSE) print_scene(stderr, &scene, SCENE_BOUND); struct draw_backend *draw = &draw_backend_opengl; draw->init(scene.size); scene_load(&scene, &src, draw); if (params->flags & PARAM_VERBOSE) print_scene(stderr, &scene, SCENE_LOADED); FILE *outfile; if (scene.outfile.len) { if (array_eq(scene.outfile, S("-"))) { outfile = stdout; } else { outfile = file_open(scene.outfile, "wb"); if (!outfile) eprintf("'%.*s': failed to open output\n", PSTR(scene.outfile)); } } else { if (isatty(STDOUT_FILENO)) eprintf("no output file specified; refusing to write video data to TTY\n"); outfile = stdout; } struct nut_writer out; nut_write_start(&out, outfile, scene.fps, scene.size); render_scene(&scene, draw, &out); nut_write_end(&out); fclose(outfile); draw->cleanup(); scene_free(&scene); source_free(&src); } static const char *help_text = "usage: %s [options] \n" " -o | --out File to write resulting .nut video to. A default may be provided by the\n" " scene file. Otherwise, video is written to stdout unless stdout is a\n" " tty.\n" " -s | --size WxH Dimensions of the video. A default may be provided by the scene file.\n" " Otherwise, 100x100 is used.\n" " -f | --fps fps Fps of the video. A default may be provided by the scene file.\n" " Otherwise, 25 is used.\n" " -D | --define key=value Set variable key to value. Variables are declared in the scene file and\n" " may or may not have defaults. Variables without defaults have to\n" " be defined via command line.\n" " -l | --live [] Enable live mode where video is rendered in real time instead of as\n" " fast as possible. A buffer time in seconds can be provided\n" " (default: 1.0). Video will be rendered ahead of time by this amount.\n" " -v | --verbose Print debugging information.\n" " -h | --help Display this help text.\n"; int main(int argc, char **argv) { struct registry reg = {}; struct params params = {}; static struct option options[] = { {"out", required_argument, 0, 'o' }, {"size", required_argument, 0, 's' }, {"fps", required_argument, 0, 'f' }, {"define", required_argument, 0, 'D' }, {"live", optional_argument, 0, 'l' }, {"verbose", no_argument, 0, 'v' }, {"help", no_argument, 0, 'h' }, {0, 0, 0, 0 } }; int c; while ((c = getopt_long(argc, argv, "o:s:f:D:l::vh", options, nullptr)) != -1) { if (c == 'l' && optarg == NULL && optind < argc && argv[optind][0] != '-') optarg = argv[optind++]; str arg; if (optarg) arg = str_intro(optarg); switch (c) { case 'o': params.flags |= PARAM_OUT; params.outfile = arg; break; case 's': { str s_w, s_h; long w, h; if (!str_split(arg, S("x"), &s_w, &s_h)) eprintf("'%.*s': size must contain an 'x'\n", PSTR(arg)); if (!str_parse_int(s_w, &w) || w <= 0) eprintf("'%.*s': invalid width\n", PSTR(s_w)); if (!str_parse_int(s_w, &h) || h <= 0) eprintf("'%.*s': invalid height\n", PSTR(s_h)); params.flags |= PARAM_SIZE; params.size[0] = w; params.size[1] = h; break; } case 'f': { long fps; if (!str_parse_int(arg, &fps) || fps <= 0) eprintf("'%.*s': invalid fps\n", PSTR(arg)); params.flags |= PARAM_FPS; params.fps = fps; break; } case 'D': { str key, val; if (!str_split(arg, S("="), &key, &val)) eprintf("'%.*s': definition must contain an '='\n", PSTR(arg)); bool succ; map_insert(®.raw_vars, key, val, &succ); if (!succ) eprintf("'%.*s': already defined\n", PSTR(key)); break; } case 'l': params.flags |= PARAM_LIVE; if (optarg) { double buffer; if (!str_parse_double(arg, &buffer) || buffer < 0) eprintf("'%.*s': invalid buffer time\n", PSTR(arg)); params.live.present = true; params.live.data = buffer; } break; case 'v': params.flags |= PARAM_VERBOSE; break; case 'h': fprintf(stderr, help_text, argv[0]); exit(EXIT_SUCCESS); break; case '?': eprintf("'%s': invalid option (see -h)\n", argv[optind]); break; case ':': eprintf("'%s': missing argument (see -h)\n", argv[optind]); break; } } int remain_args = argc-optind; if (remain_args > 1) eprintf("too many arguments (see -h)\n"); if (remain_args < 1) eprintf("missing scene file (see -h)\n"); params.infile = str_intro(argv[optind]); run(®, ¶ms); return EXIT_SUCCESS; }