writing a mesh abstraction
authorJohn Tsiombikas <nuclear@member.fsf.org>
Sat, 23 Feb 2019 19:10:17 +0000 (21:10 +0200)
committerJohn Tsiombikas <nuclear@member.fsf.org>
Sat, 23 Feb 2019 19:10:17 +0000 (21:10 +0200)
src/cmesh.c [new file with mode: 0644]
src/cmesh.h [new file with mode: 0644]
src/dynarr.c [new file with mode: 0644]
src/dynarr.h [new file with mode: 0644]
src/game.c
src/gamescr.c [new file with mode: 0644]
src/menuscr.c [new file with mode: 0644]
src/opt.c
src/opt.h
src/screen.c
src/screen.h

diff --git a/src/cmesh.c b/src/cmesh.c
new file mode 100644 (file)
index 0000000..6d7b0f4
--- /dev/null
@@ -0,0 +1,208 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "opengl.h"
+#include "cmesh.h"
+
+
+struct cmesh_vattrib {
+       int nelem;      /* num elements per attribute [1, 4] */
+       float *data;    /* dynarr */
+       unsigned int vbo;
+       int vbo_valid, data_valid;
+};
+
+
+struct cmesh {
+       char *name;
+       unsigned int nverts, nfaces;
+
+       /* current value for each attribute for the immediate mode interface */
+       cgm_vec4 cur_val[CMESH_NUM_ATTR];
+
+       unsigned int buffer_objects[CMESH_NUM_ATTR + 1];
+       struct cmesh_vattrib vattr[CMESH_NUM_ATTR];
+
+       unsigned int *idata;    /* dynarr */
+       unsigned int ibo;
+       int ibo_valid, idata_valid;
+
+       /* index buffer for wireframe rendering (constructed on demand) */
+       unsigned int wire_ibo;
+       int wire_ibo_valid;
+
+       /* axis-aligned bounding box */
+       cgm_vec3 aabb_min, aabb_max;
+       int aabb_valid;
+       /* bounding sphere */
+       cgm_vec3 bsph_center;
+       float bsph_radius;
+       int bsph_valid;
+};
+
+static int sdr_loc[CMESH_NUM_ATTR] = {0, 1, 2, 3, 4, 5, 6, 7};
+
+
+/* global state */
+void cmesh_set_attrib_sdrloc(int attr, int loc)
+{
+       sdr_loc[attr] = loc;
+}
+
+int cmesh_get_attrib_sdrloc(int attr)
+{
+       return sdr_loc[attr];
+}
+
+void cmesh_clear_attrib_sdrloc(void)
+{
+       int i;
+       for(i=0; i<CMESH_NUM_ATTR; i++) {
+               sdr_loc[i] = -1;
+       }
+}
+
+/* mesh functions */
+struct cmesh *cmesh_alloc(void)
+{
+       struct cmesh *cm;
+
+       if(!(cm = malloc(sizeof *cm))) {
+               return 0;
+       }
+       if(cmesh_init(cm) == -1) {
+               free(cm);
+               return 0;
+       }
+       return cm;
+}
+
+void cmesh_free(struct cmesh *cm)
+{
+       cmesh_destroy(cm);
+       free(cm);
+}
+
+int cmesh_init(struct cmesh *cm)
+{
+       int i;
+       cgm_clear(cm);
+
+       glGenBuffers(CMESH_NUM_ATTR + 1, cm->buffer_objects);
+
+       for(i=0; i<CMESH_NUM_ATTR; i++) {
+               if(!(cm->vattr[i].data = dynarr_alloc(0, sizeof(float)))) {
+                       cmesh_destroy(cm);
+                       return -1;
+               }
+               cm->vattr[i].vbo = buffer_objects[i];
+       }
+
+       cm->ibo = buffer_objects[CMESH_NUM_ATTR];
+       if(!(cm->idata = dynarr_alloc(0, sizeof *cm->idata))) {
+               cmesh_destroy(cm);
+               return -1;
+       }
+       return 0;
+}
+
+void cmesh_destroy(struct cmesh *cm)
+{
+       int i;
+
+       for(i=0; i<CMESH_NUM_ATTR; i++) {
+               dynarr_free(cm->vattr[i].data);
+       }
+       dynarr_free(cm->idata);
+
+       glDeleteBuffers(CMESH_NUM_ATTR + 1, cm->buffer_objects);
+       if(cm->wire_ibo) {
+               glDeleteBuffers(1, &cm->wire_ibo);
+       }
+}
+
+void cmesh_clear(struct cmesh *cm)
+{
+       memset(cm, 0, sizeof *cm);
+
+       cgm_wcons(cm->cur_val + CMESH_ATTR_COLOR, 1, 1, 1, 1);
+       /* TODO */
+}
+
+void cmesh_copy(struct cmesh *cmdest, struct cmesh *cmsrc);
+
+void cmesh_set_name(struct cmesh *cm, const char *name);
+const char *cmesh_name(struct cmesh *cm);
+
+int cmesh_has_attrib(struct cmesh *cm, int attr);
+int cmesh_indexed(struct cmesh *cm);
+
+/* vdata can be 0, in which case only memory is allocated
+ * returns pointer to the attribute array
+ */
+float *cmesh_set_attrib(struct cmesh *cm, int attr, int nelem, unsigned int num,
+               const float *vdata);
+float *cmesh_attrib(struct cmesh *cm, int attr);       /* invalidates VBO */
+const float *cmesh_attrib_ro(struct cmesh *cm, int attr);      /* doesn't invalidate */
+int cmesh_attrib_count(struct cmesh *cm, int attr);
+
+/* indices can be 0, in which case only memory is allocated
+ * returns pointer to the index array
+ */
+unsigned int *cmesh_set_index(struct cmesh *cm, int num, const unsigned int *indices);
+unsigned int *cmesh_index(struct cmesh *cm);   /* invalidates IBO */
+const unsigned int *cmesh_index_ro(struct cmesh *cm);  /* doesn't invalidate */
+int cmesh_index_count(struct cmesh *cm);
+
+int get_poly_count(struct cmesh *cm);
+
+/* attr can be -1 to invalidate all attributes */
+void cmesh_invalidate_attrib(struct cmesh *cm, int attr);
+void cmesh_invalidate_index(struct cmesh *cm);
+
+int cmesh_append(struct cmesh *cmdest, struct cmesh *cmsrc);
+
+/* immediate-mode style mesh construction interface */
+int cmesh_vertex(struct cmesh *cm, float x, float y, float z);
+int cmesh_normal(struct cmesh *cm, float nx, float ny, float nz);
+int cmesh_tangent(struct cmesh *cm, float tx, float ty, float tz);
+int cmesh_texcoord(struct cmesh *cm, float u, float v, float w);
+int cmesh_boneweights(struct cmesh *cm, float w1, float w2, float w3, float w4);
+int cmesh_boneidx(struct cmesh *cm, int idx1, int idx2, int idx3, int idx4);
+
+/* dir_xform can be null, in which case it's calculated from xform */
+void cmesh_apply_xform(struct cmesh *cm, float *xform, float *dir_xform);
+
+void cmesh_flip(struct cmesh *cm);     /* flip faces (winding) and normals */
+void cmesh_flip_faces(struct cmesh *cm);
+void cmesh_flip_normals(struct cmesh *cm);
+
+void cmesh_explode(struct cmesh *cm);  /* undo all vertex sharing */
+
+/* this is only guaranteed to work on an exploded mesh */
+void cmesh_calc_face_normals(struct cmesh *cm);
+
+void cmesh_draw(struct cmesh *cm);
+void cmesh_draw_wire(struct cmesh *cm, float linesz);
+void cmesh_draw_vertices(struct cmesh *cm, float ptsz);
+void cmesh_draw_normals(struct cmesh *cm, float len);
+void cmesh_draw_tangents(struct cmesh *cm, float len);
+
+/* get the bounding box in local space. The result will be cached and subsequent
+ * calls will return the same box. The cache gets invalidated by any functions that
+ * can affect the vertex data
+ */
+void cmesh_aabbox(struct cmesh *cm, cgm_vec3 *vmin, cgm_vec3 *vmax);
+
+/* get the bounding sphere in local space. The result will be cached ... see above */
+float cmesh_bsphere(struct cmesh *cm, cgm_vec3 *center, float *rad);
+
+/* texture coordinate manipulation */
+void cmesh_texcoord_apply_xform(struct cmesh *cm, float *xform);
+void cmesh_texcoord_gen_plane(struct cmesh *cm, cgm_vec3 *norm, cgm_vec3 *tang);
+void cmesh_texcoord_gen_box(struct cmesh *cm);
+void cmesh_texcoord_gen_cylinder(struct cmesh *cm);
+
+int cmesh_dump(struct cmesh *cm, const char *fname);
+int cmesh_dump_file(struct cmesh *cm, FILE *fp);
+int cmesh_dump_obj(struct cmesh *cm, const char *fname);
+int cmesh_dump_obj_file(struct cmesh *cm, FILE *fp, int voffs);
diff --git a/src/cmesh.h b/src/cmesh.h
new file mode 100644 (file)
index 0000000..4531d0b
--- /dev/null
@@ -0,0 +1,117 @@
+#ifndef CMESH_H_
+#define CMESH_H_
+
+#include <stdio.h>
+#include <cgmath/cgmath.h>
+#include "dynarr.h"
+
+enum {
+       CMESH_ATTR_VERTEX,
+       CMESH_ATTR_NORMAL,
+       CMESH_ATTR_TANGENT,
+       CMESH_ATTR_TEXCOORD,
+       CMESH_ATTR_COLOR,
+       CMESH_ATTR_BONEWEIGHTS,
+       CMESH_ATTR_BONEIDX,
+       CMESH_ATTR_TEXCOORD2,
+
+       CMESH_NUM_ATTR
+};
+
+struct cmesh;
+
+/* global state */
+void cmesh_set_attrib_sdrloc(int attr, int loc);
+int cmesh_get_attrib_sdrloc(int attr);
+void cmesh_clear_attrib_sdrloc(void);
+
+/* mesh functions */
+struct cmesh *cmesh_alloc(void);
+void cmesh_free(struct cmesh *cm);
+
+int cmesh_init(struct cmesh *cm);
+void cmesh_destroy(struct cmesh *cm);
+
+void cmesh_clear(struct cmesh *cm);
+void cmesh_copy(struct cmesh *cmdest, struct cmesh *cmsrc);
+
+void cmesh_set_name(struct cmesh *cm, const char *name);
+const char *cmesh_name(struct cmesh *cm);
+
+int cmesh_has_attrib(struct cmesh *cm, int attr);
+int cmesh_indexed(struct cmesh *cm);
+
+/* vdata can be 0, in which case only memory is allocated
+ * returns pointer to the attribute array
+ */
+float *cmesh_set_attrib(struct cmesh *cm, int attr, int nelem, unsigned int num,
+               const float *vdata);
+float *cmesh_attrib(struct cmesh *cm, int attr);       /* invalidates VBO */
+const float *cmesh_attrib_ro(struct cmesh *cm, int attr);      /* doesn't invalidate */
+int cmesh_attrib_count(struct cmesh *cm, int attr);
+
+/* indices can be 0, in which case only memory is allocated
+ * returns pointer to the index array
+ */
+unsigned int *cmesh_set_index(struct cmesh *cm, int num, const unsigned int *indices);
+unsigned int *cmesh_index(struct cmesh *cm);   /* invalidates IBO */
+const unsigned int *cmesh_index_ro(struct cmesh *cm);  /* doesn't invalidate */
+int cmesh_index_count(struct cmesh *cm);
+
+int get_poly_count(struct cmesh *cm);
+
+/* attr can be -1 to invalidate all attributes */
+void cmesh_invalidate_attrib(struct cmesh *cm, int attr);
+void cmesh_invalidate_index(struct cmesh *cm);
+
+int cmesh_append(struct cmesh *cmdest, struct cmesh *cmsrc);
+
+/* immediate-mode style mesh construction interface */
+int cmesh_vertex(struct cmesh *cm, float x, float y, float z);
+int cmesh_normal(struct cmesh *cm, float nx, float ny, float nz);
+int cmesh_tangent(struct cmesh *cm, float tx, float ty, float tz);
+int cmesh_texcoord(struct cmesh *cm, float u, float v, float w);
+int cmesh_boneweights(struct cmesh *cm, float w1, float w2, float w3, float w4);
+int cmesh_boneidx(struct cmesh *cm, int idx1, int idx2, int idx3, int idx4);
+
+/* dir_xform can be null, in which case it's calculated from xform */
+void cmesh_apply_xform(struct cmesh *cm, float *xform, float *dir_xform);
+
+void cmesh_flip(struct cmesh *cm);     /* flip faces (winding) and normals */
+void cmesh_flip_faces(struct cmesh *cm);
+void cmesh_flip_normals(struct cmesh *cm);
+
+void cmesh_explode(struct cmesh *cm);  /* undo all vertex sharing */
+
+/* this is only guaranteed to work on an exploded mesh */
+void cmesh_calc_face_normals(struct cmesh *cm);
+
+void cmesh_draw(struct cmesh *cm);
+void cmesh_draw_wire(struct cmesh *cm, float linesz);
+void cmesh_draw_vertices(struct cmesh *cm, float ptsz);
+void cmesh_draw_normals(struct cmesh *cm, float len);
+void cmesh_draw_tangents(struct cmesh *cm, float len);
+
+/* get the bounding box in local space. The result will be cached and subsequent
+ * calls will return the same box. The cache gets invalidated by any functions that
+ * can affect the vertex data
+ */
+void cmesh_aabbox(struct cmesh *cm, cgm_vec3 *vmin, cgm_vec3 *vmax);
+
+/* get the bounding sphere in local space. The result will be cached ... see above */
+float cmesh_bsphere(struct cmesh *cm, cgm_vec3 *center, float *rad);
+
+/* texture coordinate manipulation */
+void cmesh_texcoord_apply_xform(struct cmesh *cm, float *xform);
+void cmesh_texcoord_gen_plane(struct cmesh *cm, cgm_vec3 *norm, cgm_vec3 *tang);
+void cmesh_texcoord_gen_box(struct cmesh *cm);
+void cmesh_texcoord_gen_cylinder(struct cmesh *cm);
+
+int cmesh_dump(struct cmesh *cm, const char *fname);
+int cmesh_dump_file(struct cmesh *cm, FILE *fp);
+int cmesh_dump_obj(struct cmesh *cm, const char *fname);
+int cmesh_dump_obj_file(struct cmesh *cm, FILE *fp, int voffs);
+
+
+
+#endif /* CMESH_H_ */
diff --git a/src/dynarr.c b/src/dynarr.c
new file mode 100644 (file)
index 0000000..59bbf8c
--- /dev/null
@@ -0,0 +1,141 @@
+/* dynarr - dynamic resizable C array data structure
+ * author: John Tsiombikas <nuclear@member.fsf.org>
+ * license: public domain
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "dynarr.h"
+
+/* The array descriptor keeps auxilliary information needed to manipulate
+ * the dynamic array. It's allocated adjacent to the array buffer.
+ */
+struct arrdesc {
+       int nelem, szelem;
+       int max_elem;
+       int bufsz;      /* not including the descriptor */
+};
+
+#define DESC(x)                ((struct arrdesc*)((char*)(x) - sizeof(struct arrdesc)))
+
+void *dynarr_alloc(int elem, int szelem)
+{
+       struct arrdesc *desc;
+
+       if(!(desc = malloc(elem * szelem + sizeof *desc))) {
+               return 0;
+       }
+       desc->nelem = desc->max_elem = elem;
+       desc->szelem = szelem;
+       desc->bufsz = elem * szelem;
+       return (char*)desc + sizeof *desc;
+}
+
+void dynarr_free(void *da)
+{
+       if(da) {
+               free(DESC(da));
+       }
+}
+
+void *dynarr_resize(void *da, int elem)
+{
+       int newsz;
+       void *tmp;
+       struct arrdesc *desc;
+
+       if(!da) return 0;
+       desc = DESC(da);
+
+       newsz = desc->szelem * elem;
+
+       if(!(tmp = realloc(desc, newsz + sizeof *desc))) {
+               return 0;
+       }
+       desc = tmp;
+
+       desc->nelem = desc->max_elem = elem;
+       desc->bufsz = newsz;
+       return (char*)desc + sizeof *desc;
+}
+
+int dynarr_empty(void *da)
+{
+       return DESC(da)->nelem ? 0 : 1;
+}
+
+int dynarr_size(void *da)
+{
+       return DESC(da)->nelem;
+}
+
+
+void *dynarr_clear(void *da)
+{
+       return dynarr_resize(da, 0);
+}
+
+/* stack semantics */
+void *dynarr_push(void *da, void *item)
+{
+       struct arrdesc *desc;
+       int nelem;
+
+       desc = DESC(da);
+       nelem = desc->nelem;
+
+       if(nelem >= desc->max_elem) {
+               /* need to resize */
+               struct arrdesc *tmp;
+               int newsz = desc->max_elem ? desc->max_elem * 2 : 1;
+
+               if(!(tmp = dynarr_resize(da, newsz))) {
+                       fprintf(stderr, "failed to resize\n");
+                       return da;
+               }
+               da = tmp;
+               desc = DESC(da);
+               desc->nelem = nelem;
+       }
+
+       if(item) {
+               memcpy((char*)da + desc->nelem * desc->szelem, item, desc->szelem);
+       }
+       desc->nelem++;
+       return da;
+}
+
+void *dynarr_pop(void *da)
+{
+       struct arrdesc *desc;
+       int nelem;
+
+       desc = DESC(da);
+       nelem = desc->nelem;
+
+       if(!nelem) return da;
+
+       if(nelem <= desc->max_elem / 3) {
+               /* reclaim space */
+               struct arrdesc *tmp;
+               int newsz = desc->max_elem / 2;
+
+               if(!(tmp = dynarr_resize(da, newsz))) {
+                       fprintf(stderr, "failed to resize\n");
+                       return da;
+               }
+               da = tmp;
+               desc = DESC(da);
+               desc->nelem = nelem;
+       }
+       desc->nelem--;
+
+       return da;
+}
+
+void *dynarr_finalize(void *da)
+{
+       struct arrdesc *desc = DESC(da);
+       memmove(desc, da, desc->bufsz);
+       return desc;
+}
diff --git a/src/dynarr.h b/src/dynarr.h
new file mode 100644 (file)
index 0000000..8690b5a
--- /dev/null
@@ -0,0 +1,80 @@
+/* dynarr - dynamic resizable C array data structure
+ * author: John Tsiombikas <nuclear@member.fsf.org>
+ * license: public domain
+ */
+#ifndef DYNARR_H_
+#define DYNARR_H_
+
+/* usage example:
+ * -------------
+ * int *arr = dynarr_alloc(0, sizeof *arr);
+ *
+ * int x = 10;
+ * arr = dynarr_push(arr, &x);
+ * x = 5;
+ * arr = dynarr_push(arr, &x);
+ * x = 42;
+ * arr = dynarr_push(arr, &x);
+ *
+ * for(i=0; i<dynarr_size(arr); i++) {
+ *     printf("%d\n", arr[i]);
+ *  }
+ *  dynarr_free(arr);
+ */
+
+void *dynarr_alloc(int elem, int szelem);
+void dynarr_free(void *da);
+void *dynarr_resize(void *da, int elem);
+
+/* dynarr_empty returns non-zero if the array is empty
+ * Complexity: O(1) */
+int dynarr_empty(void *da);
+/* dynarr_size returns the number of elements in the array
+ * Complexity: O(1) */
+int dynarr_size(void *da);
+
+void *dynarr_clear(void *da);
+
+/* stack semantics */
+void *dynarr_push(void *da, void *item);
+void *dynarr_pop(void *da);
+
+/* Finalize the array. No more resizing is possible after this call.
+ * Use free() instead of dynarr_free() to deallocate a finalized array.
+ * Returns pointer to the finalized array.
+ * dynarr_finalize can't fail.
+ * Complexity: O(n)
+ */
+void *dynarr_finalize(void *da);
+
+/* helper macros */
+#define DYNARR_RESIZE(da, n) \
+       do { (da) = dynarr_resize((da), (n)); } while(0)
+#define DYNARR_CLEAR(da) \
+       do { (da) = dynarr_clear(da); } while(0)
+#define DYNARR_PUSH(da, item) \
+       do { (da) = dynarr_push((da), (item)); } while(0)
+#define DYNARR_POP(da) \
+       do { (da) = dynarr_pop(da); } while(0)
+
+/* utility macros to push characters to a string. assumes and maintains
+ * the invariant that the last element is always a zero
+ */
+#define DYNARR_STRPUSH(da, c) \
+       do { \
+               char cnull = 0, ch = (char)(c); \
+               (da) = dynarr_pop(da); \
+               (da) = dynarr_push((da), &ch); \
+               (da) = dynarr_push((da), &cnull); \
+       } while(0)
+
+#define DYNARR_STRPOP(da) \
+       do { \
+               char cnull = 0; \
+               (da) = dynarr_pop(da); \
+               (da) = dynarr_pop(da); \
+               (da) = dynarr_push((da), &cnull); \
+       } while(0)
+
+
+#endif /* DYNARR_H_ */
index 00f81cc..d4a7fe3 100644 (file)
@@ -7,6 +7,8 @@
 #include "osd.h"
 #include "opt.h"
 
+#define DEFSCR "game"
+
 static void calc_framerate(void);
 static void print_framerate(void);
 
@@ -37,6 +39,7 @@ int game_init(int argc, char **argv)
                goatvr_startvr();
                should_swap = goatvr_should_swap();
        }
+
        return 0;
 }
 
diff --git a/src/gamescr.c b/src/gamescr.c
new file mode 100644 (file)
index 0000000..de314ae
--- /dev/null
@@ -0,0 +1,76 @@
+#include "screen.h"
+
+static int init(void);
+static void cleanup(void);
+static void start(void);
+static void stop(void);
+static void update(float dt);
+static void draw(void);
+static void reshape(int x, int y);
+static void keyboard(int key, int pressed);
+static void mouse(int bn, int pressed, int x, int y);
+static void motion(int x, int y);
+static void wheel(int dir);
+
+struct game_screen game_screen = {
+       "game",
+       1,      /* opaque */
+       0,      /* next */
+       init,
+       cleanup,
+       start,
+       stop,
+       update,
+       draw,
+       reshape,
+       keyboard,
+       mouse,
+       motion,
+       wheel
+};
+
+
+static int init(void)
+{
+       return 0;
+}
+
+static void cleanup(void)
+{
+}
+
+static void start(void)
+{
+}
+
+static void stop(void)
+{
+}
+
+static void update(float dt)
+{
+}
+
+static void draw(void)
+{
+}
+
+static void reshape(int x, int y)
+{
+}
+
+static void keyboard(int key, int pressed)
+{
+}
+
+static void mouse(int bn, int pressed, int x, int y)
+{
+}
+
+static void motion(int x, int y)
+{
+}
+
+static void wheel(int dir)
+{
+}
diff --git a/src/menuscr.c b/src/menuscr.c
new file mode 100644 (file)
index 0000000..d5efbaf
--- /dev/null
@@ -0,0 +1,76 @@
+#include "screen.h"
+
+static int init(void);
+static void cleanup(void);
+static void start(void);
+static void stop(void);
+static void update(float dt);
+static void draw(void);
+static void reshape(int x, int y);
+static void keyboard(int key, int pressed);
+static void mouse(int bn, int pressed, int x, int y);
+static void motion(int x, int y);
+static void wheel(int dir);
+
+struct game_screen main_menu_screen = {
+       "main menu",
+       1,      /* opaque */
+       0,      /* next */
+       init,
+       cleanup,
+       start,
+       stop,
+       update,
+       draw,
+       reshape,
+       keyboard,
+       mouse,
+       motion,
+       wheel
+};
+
+
+static int init(void)
+{
+       return 0;
+}
+
+static void cleanup(void)
+{
+}
+
+static void start(void)
+{
+}
+
+static void stop(void)
+{
+}
+
+static void update(float dt)
+{
+}
+
+static void draw(void)
+{
+}
+
+static void reshape(int x, int y)
+{
+}
+
+static void keyboard(int key, int pressed)
+{
+}
+
+static void mouse(int bn, int pressed, int x, int y)
+{
+}
+
+static void motion(int x, int y)
+{
+}
+
+static void wheel(int dir)
+{
+}
index 91eed5d..33d7dfb 100644 (file)
--- a/src/opt.c
+++ b/src/opt.c
@@ -5,13 +5,14 @@
 #include "opt.h"
 
 
-struct options def_opt = { 1024, 768, 0 };
+struct options def_opt = { 1024, 768, 0, "game" };
 
 enum {
        OPTCFG_SIZE,
        OPTCFG_VR,
        OPTCFG_FULLSCREEN,
        OPTCFG_WINDOWED,
+       OPTCFG_SCREEN,
        OPTCFG_HELP
 };
 
@@ -21,6 +22,7 @@ static struct optcfg_option options[] = {
        {0, "vr", OPTCFG_VR, "enable VR mode"},
        {'f', "fullscreen", OPTCFG_FULLSCREEN, "run in fullscreen mode"},
        {'w', "windowed", OPTCFG_WINDOWED, "run in windowed mode"},
+       {0, "screen", OPTCFG_SCREEN, "select starting screen"},
        {'h', "help", OPTCFG_HELP, "print usage and exit"},
        OPTCFG_OPTIONS_END
 };
@@ -66,14 +68,14 @@ static int is_enabled(struct optcfg *oc)
 
 static int opt_handler(struct optcfg *oc, int optid, void *cls)
 {
+       char *valstr;
+
        switch(optid) {
        case OPTCFG_SIZE:
-               {
-                       char *valstr = optcfg_next_value(oc);
-                       if(!valstr || sscanf(valstr, "%dx%d", &opt.width, &opt.height) != 2) {
-                               fprintf(stderr, "size must be in the form: WIDTHxHEIGHT\n");
-                               return -1;
-                       }
+               valstr = optcfg_next_value(oc);
+               if(!valstr || sscanf(valstr, "%dx%d", &opt.width, &opt.height) != 2) {
+                       fprintf(stderr, "size must be of the form: WIDTHxHEIGHT\n");
+                       return -1;
                }
                break;
 
@@ -101,6 +103,19 @@ static int opt_handler(struct optcfg *oc, int optid, void *cls)
                }
                break;
 
+       case OPTCFG_SCREEN:
+               if(!(valstr = optcfg_next_value(oc))) {
+                       fprintf(stderr, "screen name missing\n");
+                       return -1;
+               }
+               free(opt.start_scr);
+               if(!(opt.start_scr = malloc(strlen(valstr) + 1))) {
+                       perror("failed to allocate memory");
+                       return -1;
+               }
+               strcpy(opt.start_scr, valstr);
+               break;
+
        case OPTCFG_HELP:
                printf("Usage: %s [options]\nOptions:\n", argv0);
                optcfg_print_options(oc);
index c5d07e4..b0560d0 100644 (file)
--- a/src/opt.h
+++ b/src/opt.h
@@ -9,6 +9,7 @@ enum {
 struct options {
        int width, height;
        unsigned int flags;
+       char *start_scr;
 } opt, def_opt;
 
 int init_options(int argc, char **argv, const char *cfgfile);
index 6638b4e..9b80cf5 100644 (file)
@@ -1,4 +1,10 @@
+#include <string.h>
 #include "screen.h"
+#include "opt.h"
+
+/* defined in their respective screen source files */
+struct game_screen main_menu_screen;
+struct game_screen game_screen;
 
 static struct game_screen *screens[16];
 static int num_screens;
@@ -7,12 +13,22 @@ static struct game_screen *stack;
 
 int init_screens(void)
 {
-       int i;
+       int i = 0;
+
+       /* populate the screens */
+       screens[i++] = &main_menu_screen;
+       screens[i++] = &game_screen;
+       num_screens = i;
+
+       stack = screens[0];
 
        for(i=0; i<num_screens; i++) {
                if(screens[i]->init() == -1) {
                        return -1;
                }
+               if(opt.start_scr && strcmp(screens[i]->name, opt.start_scr) == 0) {
+                       stack = screens[i];
+               }
        }
        return 0;
 }
index 33897dc..95cf4df 100644 (file)
@@ -15,13 +15,12 @@ struct game_screen {
 
        void (*update)(float dt);
        void (*draw)(void);
+
        void (*reshape)(int, int);
-       /* these functions return 1 if they handled the event, or 0
-        * if it should propagate to the next screen in the stack */
-       int (*keyboard)(int, int);
-       int (*mouse)(int, int, int, int);
-       int (*motion)(int, int);
-       int (*wheel)(int dir);
+       void (*keyboard)(int, int);
+       void (*mouse)(int, int, int, int);
+       void (*motion)(int, int);
+       void (*wheel)(int dir);
 };
 
 /* this always points to the top screen on the stack