forked from bartvdbraak/blender
Render frame arg parsing, list and range support
Support a comma separated list of frames, as well as frame ranges using the '..' separator. eg: `blender my.blend --render-frame 1,2,10..40,100..200`
This commit is contained in:
parent
c084520b03
commit
16f919ea58
@ -99,7 +99,7 @@
|
||||
* \{ */
|
||||
|
||||
static bool parse_int_relative(
|
||||
const char *str, int pos, int neg,
|
||||
const char *str, const char *str_end_test, int pos, int neg,
|
||||
int *r_value, const char **r_err_msg)
|
||||
{
|
||||
char *str_end = NULL;
|
||||
@ -120,7 +120,7 @@ static bool parse_int_relative(
|
||||
}
|
||||
|
||||
|
||||
if (*str_end != '\0') {
|
||||
if (*str_end != '\0' && (str_end != str_end_test)) {
|
||||
static const char *msg = "not a number";
|
||||
*r_err_msg = msg;
|
||||
return false;
|
||||
@ -136,11 +136,48 @@ static bool parse_int_relative(
|
||||
}
|
||||
}
|
||||
|
||||
static const char *parse_int_range_sep_search(const char *str, const char *str_end_test)
|
||||
{
|
||||
const char *str_end_range = NULL;
|
||||
if (str_end_test) {
|
||||
str_end_range = memchr(str, '.', (str_end_test - str) - 1);
|
||||
if (str_end_range && (str_end_range[1] != '.')) {
|
||||
str_end_range = NULL;
|
||||
}
|
||||
}
|
||||
else {
|
||||
str_end_range = strstr(str, "..");
|
||||
if (str_end_range && (str_end_range[2] == '\0')) {
|
||||
str_end_range = NULL;
|
||||
}
|
||||
}
|
||||
return str_end_range;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a number as a range, eg: `1..4`.
|
||||
*
|
||||
* The \a str_end_range argument is a result of #parse_int_range_sep_search.
|
||||
*/
|
||||
static bool parse_int_range_relative(
|
||||
const char *str, const char *str_end_range, const char *str_end_test, int pos, int neg,
|
||||
int r_value_range[2], const char **r_err_msg)
|
||||
{
|
||||
if (parse_int_relative(str, str_end_range, pos, neg, &r_value_range[0], r_err_msg) &&
|
||||
parse_int_relative(str_end_range + 2, str_end_test, pos, neg, &r_value_range[1], r_err_msg))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool parse_int_relative_clamp(
|
||||
const char *str, int pos, int neg, int min, int max,
|
||||
const char *str, const char *str_end_test, int pos, int neg, int min, int max,
|
||||
int *r_value, const char **r_err_msg)
|
||||
{
|
||||
if (parse_int_relative(str, pos, neg, r_value, r_err_msg)) {
|
||||
if (parse_int_relative(str, str_end_test, pos, neg, r_value, r_err_msg)) {
|
||||
CLAMP(*r_value, min, max);
|
||||
return true;
|
||||
}
|
||||
@ -149,11 +186,25 @@ static bool parse_int_relative_clamp(
|
||||
}
|
||||
}
|
||||
|
||||
static bool parse_int_range_relative_clamp(
|
||||
const char *str, const char *str_end_range, const char *str_end_test, int pos, int neg, int min, int max,
|
||||
int r_value_range[2], const char **r_err_msg)
|
||||
{
|
||||
if (parse_int_range_relative(str, str_end_range, str_end_test, pos, neg, r_value_range, r_err_msg)) {
|
||||
CLAMP(r_value_range[0], min, max);
|
||||
CLAMP(r_value_range[1], min, max);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* No clamping, fails with any number outside the range.
|
||||
*/
|
||||
static bool parse_int_strict_range(
|
||||
const char *str, const int min, const int max,
|
||||
const char *str, const char *str_end_test, const int min, const int max,
|
||||
int *r_value, const char **r_err_msg)
|
||||
{
|
||||
char *str_end = NULL;
|
||||
@ -162,7 +213,7 @@ static bool parse_int_strict_range(
|
||||
errno = 0;
|
||||
value = strtol(str, &str_end, 10);
|
||||
|
||||
if (*str_end != '\0') {
|
||||
if (*str_end != '\0' && (str_end != str_end_test)) {
|
||||
static const char *msg = "not a number";
|
||||
*r_err_msg = msg;
|
||||
return false;
|
||||
@ -179,17 +230,17 @@ static bool parse_int_strict_range(
|
||||
}
|
||||
|
||||
static bool parse_int(
|
||||
const char *str,
|
||||
const char *str, const char *str_end_test,
|
||||
int *r_value, const char **r_err_msg)
|
||||
{
|
||||
return parse_int_strict_range(str, INT_MIN, INT_MAX, r_value, r_err_msg);
|
||||
return parse_int_strict_range(str, str_end_test, INT_MIN, INT_MAX, r_value, r_err_msg);
|
||||
}
|
||||
|
||||
static bool parse_int_clamp(
|
||||
const char *str, int min, int max,
|
||||
const char *str, const char *str_end_test, int min, int max,
|
||||
int *r_value, const char **r_err_msg)
|
||||
{
|
||||
if (parse_int(str, r_value, r_err_msg)) {
|
||||
if (parse_int(str, str_end_test, r_value, r_err_msg)) {
|
||||
CLAMP(*r_value, min, max);
|
||||
return true;
|
||||
}
|
||||
@ -198,6 +249,115 @@ static bool parse_int_clamp(
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/**
|
||||
* Version of #parse_int_relative_clamp
|
||||
* that parses a comma separated list of numbers.
|
||||
*/
|
||||
static int *parse_int_relative_clamp_n(
|
||||
const char *str, int pos, int neg, int min, int max,
|
||||
int *r_value_len, const char **r_err_msg)
|
||||
{
|
||||
const char sep = ',';
|
||||
int len = 1;
|
||||
for (int i = 0; str[i]; i++) {
|
||||
if (str[i] == sep) {
|
||||
len++;
|
||||
}
|
||||
}
|
||||
|
||||
int *values = MEM_mallocN(sizeof(*values) * len, __func__);
|
||||
int i = 0;
|
||||
while (true) {
|
||||
const char *str_end = strchr(str, sep);
|
||||
if ((*str == sep) || (*str == '\0')) {
|
||||
static const char *msg = "incorrect comma use";
|
||||
*r_err_msg = msg;
|
||||
goto fail;
|
||||
|
||||
}
|
||||
else if (parse_int_relative_clamp(str, str_end, pos, neg, min, max, &values[i], r_err_msg)) {
|
||||
i++;
|
||||
}
|
||||
else {
|
||||
goto fail; /* error message already set */
|
||||
}
|
||||
|
||||
if (str_end) { /* next */
|
||||
str = str_end + 1;
|
||||
}
|
||||
else { /* finished */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*r_value_len = i;
|
||||
return values;
|
||||
|
||||
fail:
|
||||
MEM_freeN(values);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Version of #parse_int_relative_clamp & #parse_int_range_relative_clamp
|
||||
* that parses a comma separated list of numbers.
|
||||
*
|
||||
* \note single values are evaluated as a range with matching start/end.
|
||||
*/
|
||||
static int (*parse_int_range_relative_clamp_n(
|
||||
const char *str, int pos, int neg, int min, int max,
|
||||
int *r_value_len, const char **r_err_msg))[2]
|
||||
{
|
||||
const char sep = ',';
|
||||
int len = 1;
|
||||
for (int i = 0; str[i]; i++) {
|
||||
if (str[i] == sep) {
|
||||
len++;
|
||||
}
|
||||
}
|
||||
|
||||
int (*values)[2] = MEM_mallocN(sizeof(*values) * len, __func__);
|
||||
int i = 0;
|
||||
while (true) {
|
||||
const char *str_end_range;
|
||||
const char *str_end = strchr(str, sep);
|
||||
if ((*str == sep) || (*str == '\0')) {
|
||||
static const char *msg = "incorrect comma use";
|
||||
*r_err_msg = msg;
|
||||
goto fail;
|
||||
}
|
||||
else if ((str_end_range = parse_int_range_sep_search(str, str_end)) ?
|
||||
parse_int_range_relative_clamp(str, str_end_range, str_end, pos, neg, min, max, values[i], r_err_msg) :
|
||||
parse_int_relative_clamp(str, str_end, pos, neg, min, max, &values[i][0], r_err_msg))
|
||||
{
|
||||
if (str_end_range == NULL) {
|
||||
values[i][1] = values[i][0];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
else {
|
||||
goto fail; /* error message already set */
|
||||
}
|
||||
|
||||
if (str_end) { /* next */
|
||||
str = str_end + 1;
|
||||
}
|
||||
else { /* finished */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*r_value_len = i;
|
||||
return values;
|
||||
|
||||
fail:
|
||||
MEM_freeN(values);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
|
||||
@ -632,7 +792,7 @@ static int arg_handle_debug_value_set(int argc, const char **argv, void *UNUSED(
|
||||
if (argc > 1) {
|
||||
const char *err_msg = NULL;
|
||||
int value;
|
||||
if (!parse_int(argv[1], &value, &err_msg)) {
|
||||
if (!parse_int(argv[1], NULL, &value, &err_msg)) {
|
||||
printf("\nError: %s '%s %s'.\n", err_msg, arg_id, argv[1]);
|
||||
return 1;
|
||||
}
|
||||
@ -736,7 +896,7 @@ static int arg_handle_window_geometry(int argc, const char **argv, void *UNUSED(
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
const char *err_msg = NULL;
|
||||
if (!parse_int(argv[i + 1], ¶ms[i], &err_msg)) {
|
||||
if (!parse_int(argv[i + 1], NULL, ¶ms[i], &err_msg)) {
|
||||
printf("\nError: %s '%s %s'.\n", err_msg, arg_id, argv[1]);
|
||||
exit(1);
|
||||
}
|
||||
@ -981,7 +1141,7 @@ static int arg_handle_threads_set(int argc, const char **argv, void *UNUSED(data
|
||||
if (argc > 1) {
|
||||
const char *err_msg = NULL;
|
||||
int threads;
|
||||
if (!parse_int_strict_range(argv[1], min, max, &threads, &err_msg)) {
|
||||
if (!parse_int_strict_range(argv[1], NULL, min, max, &threads, &err_msg)) {
|
||||
printf("\nError: %s '%s %s', expected number in [%d..%d].\n", err_msg, arg_id, argv[1], min, max);
|
||||
return 1;
|
||||
}
|
||||
@ -1014,7 +1174,7 @@ static int arg_handle_verbosity_set(int argc, const char **argv, void *UNUSED(da
|
||||
if (argc > 1) {
|
||||
const char *err_msg = NULL;
|
||||
int level;
|
||||
if (!parse_int(argv[1], &level, &err_msg)) {
|
||||
if (!parse_int(argv[1], NULL, &level, &err_msg)) {
|
||||
printf("\nError: %s '%s %s'.\n", err_msg, arg_id, argv[1]);
|
||||
}
|
||||
|
||||
@ -1132,7 +1292,12 @@ static int arg_handle_ge_parameters_set(int argc, const char **argv, void *data)
|
||||
}
|
||||
|
||||
static const char arg_handle_render_frame_doc[] =
|
||||
"<frame>\n\tRender frame <frame> and save it.\n\t+<frame> start frame relative, -<frame> end frame relative."
|
||||
"<frame>\n"
|
||||
"\tRender frame <frame> and save it.\n"
|
||||
"\n"
|
||||
"\t* +<frame> start frame relative, -<frame> end frame relative.\n"
|
||||
"\t* A comma separated list of frames can also be used (no spaces).\n"
|
||||
"\t* A range of frames can be expressed using '..' seperator between the first and last frames (inclusive).\n"
|
||||
;
|
||||
static int arg_handle_render_frame(int argc, const char **argv, void *data)
|
||||
{
|
||||
@ -1145,12 +1310,12 @@ static int arg_handle_render_frame(int argc, const char **argv, void *data)
|
||||
if (argc > 1) {
|
||||
const char *err_msg = NULL;
|
||||
Render *re;
|
||||
int frame;
|
||||
ReportList reports;
|
||||
|
||||
if (!parse_int_relative_clamp(
|
||||
argv[1], scene->r.sfra, scene->r.efra, MINAFRAME, MAXFRAME,
|
||||
&frame, &err_msg))
|
||||
int (*frame_range_arr)[2], frames_range_len;
|
||||
if ((frame_range_arr = parse_int_range_relative_clamp_n(
|
||||
argv[1], scene->r.sfra, scene->r.efra, MINAFRAME, MAXFRAME,
|
||||
&frames_range_len, &err_msg)) == NULL)
|
||||
{
|
||||
printf("\nError: %s '%s %s'.\n", err_msg, arg_id, argv[1]);
|
||||
return 1;
|
||||
@ -1161,9 +1326,20 @@ static int arg_handle_render_frame(int argc, const char **argv, void *data)
|
||||
BKE_reports_init(&reports, RPT_PRINT);
|
||||
|
||||
RE_SetReports(re, &reports);
|
||||
RE_BlenderAnim(re, bmain, scene, NULL, scene->lay, frame, frame, scene->r.frame_step);
|
||||
for (int i = 0; i < frames_range_len; i++) {
|
||||
/* We could pass in frame ranges,
|
||||
* but prefer having exact behavior as passing in multiple frames */
|
||||
if ((frame_range_arr[i][0] < frame_range_arr[i][1]) == 0) {
|
||||
printf("\nWarning: negative range ignored '%s %s'.\n", arg_id, argv[1]);
|
||||
}
|
||||
|
||||
for (int frame = frame_range_arr[i][0]; frame <= frame_range_arr[i][1]; frame++) {
|
||||
RE_BlenderAnim(re, bmain, scene, NULL, scene->lay, frame, frame, scene->r.frame_step);
|
||||
}
|
||||
}
|
||||
RE_SetReports(re, NULL);
|
||||
BLI_end_threaded_malloc();
|
||||
MEM_freeN(frame_range_arr);
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
@ -1233,7 +1409,7 @@ static int arg_handle_frame_start_set(int argc, const char **argv, void *data)
|
||||
if (argc > 1) {
|
||||
const char *err_msg = NULL;
|
||||
if (!parse_int_relative_clamp(
|
||||
argv[1], scene->r.sfra, scene->r.sfra - 1, MINAFRAME, MAXFRAME,
|
||||
argv[1], NULL, scene->r.sfra, scene->r.sfra - 1, MINAFRAME, MAXFRAME,
|
||||
&scene->r.sfra, &err_msg))
|
||||
{
|
||||
printf("\nError: %s '%s %s'.\n", err_msg, arg_id, argv[1]);
|
||||
@ -1264,7 +1440,7 @@ static int arg_handle_frame_end_set(int argc, const char **argv, void *data)
|
||||
if (argc > 1) {
|
||||
const char *err_msg = NULL;
|
||||
if (!parse_int_relative_clamp(
|
||||
argv[1], scene->r.efra, scene->r.efra - 1, MINAFRAME, MAXFRAME,
|
||||
argv[1], NULL, scene->r.efra, scene->r.efra - 1, MINAFRAME, MAXFRAME,
|
||||
&scene->r.efra, &err_msg))
|
||||
{
|
||||
printf("\nError: %s '%s %s'.\n", err_msg, arg_id, argv[1]);
|
||||
@ -1293,7 +1469,7 @@ static int arg_handle_frame_skip_set(int argc, const char **argv, void *data)
|
||||
if (scene) {
|
||||
if (argc > 1) {
|
||||
const char *err_msg = NULL;
|
||||
if (!parse_int_clamp(argv[1], 1, MAXFRAME, &scene->r.frame_step, &err_msg)) {
|
||||
if (!parse_int_clamp(argv[1], NULL, 1, MAXFRAME, &scene->r.frame_step, &err_msg)) {
|
||||
printf("\nError: %s '%s %s'.\n", err_msg, arg_id, argv[1]);
|
||||
}
|
||||
return 1;
|
||||
@ -1444,7 +1620,7 @@ static int arg_handle_python_exit_code_set(int argc, const char **argv, void *UN
|
||||
const char *err_msg = NULL;
|
||||
const int min = 0, max = 255;
|
||||
int exit_code;
|
||||
if (!parse_int_strict_range(argv[1], min, max, &exit_code, &err_msg)) {
|
||||
if (!parse_int_strict_range(argv[1], NULL, min, max, &exit_code, &err_msg)) {
|
||||
printf("\nError: %s '%s %s', expected number in [%d..%d].\n", err_msg, arg_id, argv[1], min, max);
|
||||
return 1;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user