From 92fb08c674a95e1b14de3dc21c718444be91aac5 Mon Sep 17 00:00:00 2001 From: John Tsiombikas Date: Mon, 21 Jun 2021 08:23:39 +0300 Subject: [PATCH] added optcfg and fixed camera xform --- src/game.c | 51 +++++++++ src/game.h | 7 ++ src/main.c | 55 ++++----- src/optcfg.c | 350 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/optcfg.h | 90 +++++++++++++++ 5 files changed, 517 insertions(+), 36 deletions(-) create mode 100644 src/optcfg.c create mode 100644 src/optcfg.h diff --git a/src/game.c b/src/game.c index b314e49..3ea6ca8 100644 --- a/src/game.c +++ b/src/game.c @@ -1,3 +1,54 @@ #include "game.h" +#include "optcfg.h" struct level lvl; +struct options opt; + +enum { OPT_SIZE, OPT_HELP }; + +static struct optcfg_option options[] = { + {'s', "size", OPT_SIZE, "rendering resolution (WxH)"}, + {'h', "help", OPT_HELP, "print usage and exit"}, + OPTCFG_OPTIONS_END +}; + +static int opt_handler(struct optcfg *o, int opt, void *cls); + +int init_options(int argc, char **argv) +{ + struct optcfg *optcfg; + + opt.width = 1280; + opt.height = 800; + + optcfg = optcfg_init(options); + optcfg_set_opt_callback(optcfg, opt_handler, argv[0]); + optcfg_parse_config_file(optcfg, "cyberay.conf"); + if(optcfg_parse_args(optcfg, argc, argv) == -1) { + return -1; + } + + optcfg_destroy(optcfg); + return 0; +} + +static int opt_handler(struct optcfg *o, int optid, void *cls) +{ + char *val; + + switch(optid) { + case OPT_SIZE: + if(!(val = optcfg_next_value(o)) || sscanf(val, "%dx%d", &opt.width, &opt.height) != 2) { + fprintf(stderr, "size: expected x\n"); + return -1; + } + break; + + case OPT_HELP: + printf("Usage: %s [options]\n", (char*)cls); + printf("Options:\n"); + optcfg_print_options(o); + exit(0); + } + return 0; +} diff --git a/src/game.h b/src/game.h index 0e263bf..c61ccb0 100644 --- a/src/game.h +++ b/src/game.h @@ -3,6 +3,13 @@ #include "level.h" +struct options { + int width, height; +}; + extern struct level lvl; +extern struct options opt; + +int init_options(int argc, char **argv); #endif /* GAME_H_ */ diff --git a/src/main.c b/src/main.c index d2240e0..51faaa0 100644 --- a/src/main.c +++ b/src/main.c @@ -49,7 +49,7 @@ static unsigned int nextpow2(unsigned int x); static long start_time; static float cam_theta, cam_phi; -static cgm_vec3 cam_pos = {0, -1.6, 0}; +static cgm_vec3 cam_pos = {0, 1.6, 0}; static int mouse_x, mouse_y; static int bnstate[8]; @@ -73,8 +73,13 @@ static float tex_xform[16]; int main(int argc, char **argv) { glutInit(&argc, argv); - glutInitWindowSize(1280, 800); - glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE); + + if(init_options(argc, argv) == -1) { + return 1; + } + + glutInitWindowSize(opt.width, opt.height); + glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); glutCreateWindow("cyberay"); glutDisplayFunc(display); @@ -106,11 +111,6 @@ static int init(void) glEnable(GL_CULL_FACE); - /* - glEnable(GL_DEPTH_TEST); - glEnable(GL_LIGHTING); - glEnable(GL_LIGHT0); - */ glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); @@ -156,19 +156,19 @@ static void update(void) vfwd += WALK_SPEED * dt; } if(inpstate[INP_RIGHT]) { - vright -= WALK_SPEED * dt; + vright += WALK_SPEED * dt; } if(inpstate[INP_LEFT]) { - vright += WALK_SPEED * dt; + vright -= WALK_SPEED * dt; } cam_pos.x += cos(cam_theta) * vright + sin(cam_theta) * vfwd; - cam_pos.z += sin(cam_theta) * vright - cos(cam_theta) * vfwd; + cam_pos.z += -sin(cam_theta) * vright + cos(cam_theta) * vfwd; cgm_midentity(view_xform); - cgm_mtranslate(view_xform, cam_pos.x, cam_pos.y, cam_pos.z); - cgm_mrotate_y(view_xform, cam_theta); cgm_mrotate_x(view_xform, cam_phi); + cgm_mrotate_y(view_xform, cam_theta); + cgm_mtranslate(view_xform, cam_pos.x, cam_pos.y, cam_pos.z); } static void display(void) @@ -181,25 +181,16 @@ static void display(void) glEnable(GL_TEXTURE_2D); glBegin(GL_QUADS); - glTexCoord2f(0, 0); + glTexCoord2f(0, 1); glVertex2f(-1, -1); - glTexCoord2f(1, 0); - glVertex2f(1, -1); glTexCoord2f(1, 1); + glVertex2f(1, -1); + glTexCoord2f(1, 0); glVertex2f(1, 1); - glTexCoord2f(0, 1); + glTexCoord2f(0, 0); glVertex2f(-1, 1); glEnd(); - /* - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - glMatrixMode(GL_MODELVIEW); - glLoadMatrixf(view_xform); - - draw_level(&lvl); - */ - glutSwapBuffers(); assert(glGetError() == GL_NO_ERROR); } @@ -212,14 +203,6 @@ static void idle(void) static void reshape(int x, int y) { glViewport(0, 0, x, y); - /* - float proj[16]; - - cgm_mperspective(proj, cgm_deg_to_rad(50.0f), (float)x / (float)y, 0.5, 500.0); - - glMatrixMode(GL_PROJECTION); - glLoadMatrixf(proj); - */ if(x > tex_width || y > tex_height) { tex_width = nextpow2(x); @@ -284,8 +267,8 @@ static void motion(int x, int y) if(!(dx | dy)) return; if(bnstate[0]) { - cam_theta += dx * 0.01; - cam_phi += dy * 0.01; + cam_theta -= dx * 0.01; + cam_phi -= dy * 0.01; if(cam_phi < -M_PI) cam_phi = -M_PI; if(cam_phi > M_PI) cam_phi = M_PI; diff --git a/src/optcfg.c b/src/optcfg.c new file mode 100644 index 0000000..7260bbe --- /dev/null +++ b/src/optcfg.c @@ -0,0 +1,350 @@ +#include +#include +#include +#include +#ifdef WIN32 +#include +#else +#include +#endif +#include "optcfg.h" + +struct optcfg { + struct optcfg_option *optlist; + + optcfg_opt_callback opt_func; + void *opt_cls; + optcfg_arg_callback arg_func; + void *arg_cls; + + int err_abort; + + /* argument parsing state */ + char **argv; + int argidx; + int disable_opt; + + /* config file parsing state */ + const char *cfg_fname; + int cfg_nline; + char *cfg_value; +}; + +static int get_opt(struct optcfg *oc, const char *s, int *disable_opt); +static char *skip_spaces(char *s); +static void strip_comments(char *s); +static void strip_trailing_spaces(char *s); +static char *parse_keyval(char *line); + + +struct optcfg *optcfg_init(struct optcfg_option *optv) +{ + struct optcfg *oc; + + if(!(oc = calloc(1, sizeof *oc))) { + return 0; + } + oc->optlist = optv; + oc->err_abort = 1; + return oc; +} + +void optcfg_destroy(struct optcfg *oc) +{ + memset(oc, 0, sizeof *oc); + free(oc); +} + +void optcfg_set_opt_callback(struct optcfg *oc, optcfg_opt_callback func, void *cls) +{ + oc->opt_func = func; + oc->opt_cls = cls; +} + +void optcfg_set_arg_callback(struct optcfg *oc, optcfg_arg_callback func, void *cls) +{ + oc->arg_func = func; + oc->arg_cls = cls; +} + +void optcfg_set_error_action(struct optcfg *oc, int act) +{ + if(act == OPTCFG_ERROR_FAIL) { + oc->err_abort = 1; + } else if(act == OPTCFG_ERROR_IGNORE) { + oc->err_abort = 0; + } +} + +int optcfg_parse_args(struct optcfg *oc, int argc, char **argv) +{ + int i; + + oc->argv = argv; + + for(i=1; iargidx = i; + + if(argv[i][0] == '-') { + if(oc->opt_func) { + int o = get_opt(oc, argv[i], &oc->disable_opt); + if(o == -1 || oc->opt_func(oc, o, oc->opt_cls) == -1) { + if(oc->err_abort) { + fprintf(stderr, "unexpected option: %s\n", argv[i]); + return -1; + } + } + } else { + fprintf(stderr, "unexpected option: %s\n", argv[i]); + if(oc->err_abort) { + return -1; + } + } + } else { + if(oc->arg_func) { + if(oc->arg_func(oc, argv[i], oc->arg_cls) == -1) { + if(oc->err_abort) { + fprintf(stderr, "unexpected argument: %s\n", argv[i]); + return -1; + } + } + } else { + fprintf(stderr, "unexpected argument: %s\n", argv[i]); + if(oc->err_abort) { + return -1; + } + } + } + + i = oc->argidx; + } + + oc->argidx = 0; /* done parsing args */ + return 0; +} + +int optcfg_parse_config_file(struct optcfg *oc, const char *fname) +{ + int res; + FILE *fp = fopen(fname, "rb"); + if(!fp) { + return -1; + } + + oc->cfg_fname = fname; + res = optcfg_parse_config_stream(oc, fp); + oc->cfg_fname = 0; + fclose(fp); + return res; +} + +int optcfg_parse_config_stream(struct optcfg *oc, FILE *fp) +{ + char buf[512]; + + oc->cfg_nline = 0; + while(fgets(buf, sizeof buf, fp)) { + ++oc->cfg_nline; + + if(optcfg_parse_config_line(oc, buf) == -1) { + if(oc->err_abort) { + return -1; + } + } + } + return 0; +} + +int optcfg_parse_config_line(struct optcfg *oc, const char *line) +{ + int opt, len; + char *start, *val, *buf; + + len = strlen(line); + buf = alloca(len + 1); + memcpy(buf, line, len + 1); + + start = skip_spaces(buf); + strip_comments(start); + strip_trailing_spaces(start); + if(!*start) { + return 0; + } + + if(!(val = parse_keyval(start))) { + fprintf(stderr, "error parsing %s line %d: invalid syntax\n", oc->cfg_fname ? oc->cfg_fname : "", oc->cfg_nline); + return -1; + } + oc->cfg_value = val; + if((opt = get_opt(oc, start, 0)) == -1) { + fprintf(stderr, "error parsing %s line %d: unknown option: %s\n", oc->cfg_fname ? oc->cfg_fname : "", + oc->cfg_nline, start); + return -1; + } + + if(oc->opt_func) { + if(oc->opt_func(oc, opt, oc->opt_cls) == -1) { + return -1; + } + } + oc->cfg_value = 0; + return 0; +} + +int optcfg_enabled_value(struct optcfg *oc, int *enabledp) +{ + if(oc->argidx) { + *enabledp = ~oc->disable_opt & 1; + } else { + char *val = optcfg_next_value(oc); + if(optcfg_bool_value(val, enabledp) == -1) { + return -1; + } + } + return 0; +} + + +char *optcfg_next_value(struct optcfg *oc) +{ + if(oc->argidx) { /* we're in the middle of parsing arguments, so get the next one */ + return oc->argv[++oc->argidx]; + } + if(oc->cfg_value) { + char *val = oc->cfg_value; + oc->cfg_value = 0; + return val; + } + return 0; +} + +void optcfg_print_options(struct optcfg *oc) +{ + int i; + for(i=0; oc->optlist[i].opt != -1; i++) { + struct optcfg_option *opt = oc->optlist + i; + + if(opt->c) { + printf(" -%c", opt->c); + } else { + printf(" "); + } + if(opt->s) { + printf("%c-%s: ", opt->c ? ',' : ' ', opt->s); + } else { + printf(": "); + } + printf("%s\n", opt->desc ? opt->desc : "undocumented"); + } +} + +int optcfg_bool_value(char *s, int *valret) +{ + if(strcasecmp(s, "yes") == 0 || strcasecmp(s, "true") == 0 || strcmp(s, "1") == 0) { + *valret = 1; + return 0; + } + if(strcasecmp(s, "no") == 0 || strcasecmp(s, "false") == 0 || strcmp(s, "0") == 0) { + *valret = 0; + return 0; + } + return -1; +} + +int optcfg_int_value(char *str, int *valret) +{ + char *endp; + *valret = strtol(str, &endp, 0); + if(endp == str) { + return -1; + } + return 0; +} + +int optcfg_float_value(char *str, float *valret) +{ + char *endp; + *valret = strtod(str, &endp); + if(endp == str) { + return -1; + } + return 0; +} + + + +static int get_opt(struct optcfg *oc, const char *arg, int *disable_opt) +{ + int i, ndashes = 0; + + while(*arg && *arg == '-') { + ++ndashes; + ++arg; + } + + if(ndashes > 2) { + arg -= ndashes; + ndashes = 0; + } + + if(disable_opt) { + if(ndashes && (strstr(arg, "no-") == arg || strstr(arg, "disable-") == arg)) { + *disable_opt = 1; + arg = strchr(arg, '-') + 1; /* guaranteed to exist at this point */ + } else { + *disable_opt = 0; + } + } + + if(arg[1]) { /* match long options */ + for(i=0; oc->optlist[i].opt != -1; i++) { + if(strcmp(arg, oc->optlist[i].s) == 0) { + return i; + } + } + } else { + for(i=0; oc->optlist[i].opt != -1; i++) { + if(arg[0] == oc->optlist[i].c) { + return i; + } + } + } + return -1; +} + +static char *skip_spaces(char *s) +{ + while(*s && isspace(*s)) ++s; + return s; +} + +static void strip_comments(char *s) +{ + while(*s && *s != '#') ++s; + if(*s == '#') *s = 0; +} + +static void strip_trailing_spaces(char *s) +{ + char *end = s + strlen(s) - 1; + while(end >= s && isspace(*end)) { + *end-- = 0; + } +} + +static char *parse_keyval(char *line) +{ + char *val; + char *eq = strchr(line, '='); + if(!eq) return 0; + + *eq = 0; + strip_trailing_spaces(line); + val = skip_spaces(eq + 1); + strip_trailing_spaces(val); + + if(!*line || !*val) { + return 0; + } + return val; +} diff --git a/src/optcfg.h b/src/optcfg.h new file mode 100644 index 0000000..b6d060a --- /dev/null +++ b/src/optcfg.h @@ -0,0 +1,90 @@ +/* generic unified commandline option and config file parsing library */ +#ifndef LIBOPTCFG_H_ +#define LIBOPTCFG_H_ + +#include + +struct optcfg; + +struct optcfg_option { + char c; /* short (optional): used only for argument parsing */ + const char *s; /* long: used for long options and config files */ + int opt; /* the corresponding option enumeration */ + const char *desc; /* text description for printing usage information */ +}; + +#define OPTCFG_OPTIONS_END {0, 0, -1, 0} + +typedef int (*optcfg_opt_callback)(struct optcfg *oc, int opt, void *cls); +typedef int (*optcfg_arg_callback)(struct optcfg *oc, const char *arg, void *cls); + +#ifdef __cplusplus +extern "C" { +#endif + +/* initialize the optcfg object with a valid option vector terminated by an + * entry with an opt value of -1 (other fields ignored for termination purposes) + * + * Example: + * struct optcfg_option options[] = { + * {'f', "foo", OPT_FOO, "Makes sure the foo is bar"}, + * {'h', "help", OPT_HELP, "Print usage information and exit"}, + * OPTCFG_OPTIONS_END + * }; + * struct optcfg *oc = optcfg_init(options); + */ +struct optcfg *optcfg_init(struct optcfg_option *optv); +void optcfg_destroy(struct optcfg *oc); + +/* The parse_* functions call the option callback for each option. + * + * The option callback can then call optcfg_next_value to retrieve any + * values attached to this option. When optcfg_next_value returns 0, there + * are no more values available. + * The option callback must return 0 for success, and -1 to abort parsing. + */ +void optcfg_set_opt_callback(struct optcfg *oc, optcfg_opt_callback func, void *cls); +/* the argument callback is only called from optcfg_parse_args(), when a non-option + * argument is encountered (an argument not starting with a dash) + */ +void optcfg_set_arg_callback(struct optcfg *oc, optcfg_arg_callback func, void *cls); + +enum { OPTCFG_ERROR_FAIL, OPTCFG_ERROR_IGNORE }; +void optcfg_set_error_action(struct optcfg *oc, int act); + +int optcfg_parse_args(struct optcfg *oc, int argc, char **argv); +int optcfg_parse_config_file(struct optcfg *oc, const char *fname); +int optcfg_parse_config_stream(struct optcfg *oc, FILE *fp); +int optcfg_parse_config_line(struct optcfg *oc, const char *line); +/* TODO custom I/O callback version of config file parsing */ + +/* special value function which returns if the option is enabled or disabled + * For config files it works similar to calling optcfg_next_value, and + * optcfg_bool_value in sequence. + * For argument parsing however, it doesn't consume further arguments. Merely + * the presence of the option makes it enabled, and its presence with a -no- + * or -disable- prefix disables it. + */ +int optcfg_enabled_value(struct optcfg *oc, int *enabledp); + +/* call optcfg_next_value in the option callback to retrieve the next value + * of the current option. returns 0 if there is no next value. + */ +char *optcfg_next_value(struct optcfg *oc); + +/* helper function which can be used to print the available options */ +void optcfg_print_options(struct optcfg *oc); + +/* helper functions to convert value strings to typed values + * returns 0 for success and value is returned through the valret pointer, + * otherwise it returns -1 for type mismatch, and valret contents are undefined + */ +int optcfg_bool_value(char *str, int *valret); /* accepts yes/no, true/false, 1/0 */ +int optcfg_int_value(char *str, int *valret); +int optcfg_float_value(char *str, float *valret); + +#ifdef __cplusplus +} +#endif + +#endif /* LIBOPTCFG_H_ */ -- 1.7.10.4