polyadvent

A game engine from scratch in C
git clone git://jb55.com/polyadvent
Log | Files | Refs | README

commit 2522f790272739f4933294c7c357db25e5e993f1
parent 23463bc3c2520d6a058de526e0e6edd4043899e4
Author: William Casarin <jb55@jb55.com>
Date:   Sun,  7 Jul 2019 00:19:43 -0700

model compiler progress

Diffstat:
M.gitignore | 1+
MMakefile | 6+++---
Msrc/animation.h | 1-
Asrc/dae.c | 270+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/dae.h | 11+++++++++++
Msrc/model.c | 4++++
Msrc/model.h | 4++++
Dtest/test_animation.c | 48------------------------------------------------
Atest/test_dae.c | 48++++++++++++++++++++++++++++++++++++++++++++++++
Mtools/compile-model.c | 240+++++--------------------------------------------------------------------------
10 files changed, 355 insertions(+), 278 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -10,3 +10,4 @@ polyadvent *.zip /tools/compile-model /samples.bin +/test/test_dae diff --git a/Makefile b/Makefile @@ -18,7 +18,7 @@ DAES=$(wildcard data/models/*.dae) MODELS=$(DAES:.dae=.mdl) OBJS=$(SRCS:.c=.o) -TESTS = test/test_animation +TESTS = test/test_dae TESTS += test/test_resource TESTS += test/test_scene @@ -31,7 +31,7 @@ clean: include $(OBJS:.o=.d) include main.d -include test/test_animation.d +include test/test_dae.d %.d: %.c @rm -f $@; \ @@ -51,7 +51,7 @@ data/models/%.mdl: data/models/%.dae tools/compile-model ./tools/compile-model $< $@ check: $(TESTS) - ./test/test_animation + ./test/test_dae ./test/test_resource ./test/test_scene diff --git a/src/animation.h b/src/animation.h @@ -17,7 +17,6 @@ struct joint node_id node_id; }; - struct pose { struct joint joints[MAX_JOINTS]; diff --git a/src/dae.c b/src/dae.c @@ -0,0 +1,270 @@ + +#include "xml.h" +#include "util.h" +#include "animation.h" +#include "dae.h" + +enum dae_state { + PARSING_START, + PARSING_NODE, + PARSING_FLOAT_ARRAY, + PARSING_POSE, + PARSING_JOINT, + PARSING_JOINT_MATRIX, + PARSING_WEIGHTS, + PARSING_POSITIONS, + PARSING_COLORS, + PARSING_NORMALS, +}; + +struct dae_data { + int node_level; + int state; + FILE *dae_file; + struct model *model; + struct make_geometry *geom; + int counter; + char current_name[JOINT_LABEL_SIZE]; +}; + + +static void parse_joint(const char *t, int id, struct joint *joint) +{ + struct node *node = get_node(&joint->node_id); + assert(node); + + float *m = node->mat; + joint->id = id; + /* printf(" parsing joint %d: %s\n", id, t); */ + + sscanf(t, "%f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f", + &m[0], &m[1], &m[2], &m[3], + &m[4], &m[5], &m[6], &m[7], + &m[8], &m[9], &m[10], &m[11], + &m[12], &m[13], &m[14], &m[15] + ); +} + + +static void dae_tag_start(struct xmlparser *x, const char *t, size_t tl) +{ + struct dae_data *data = (struct dae_data*)x->user_data; + + if (streq(t, "node")) { + data->state = PARSING_NODE; + data->node_level++; + } + else if (streq(t, "float_array")) { + data->state = PARSING_FLOAT_ARRAY; + } + else if (data->state == PARSING_JOINT && streq(t, "matrix")) + data->state = PARSING_JOINT_MATRIX; + else + return; +} + +static void dae_tag_end(struct xmlparser *x, const char *t, size_t tl, int what) +{ + struct dae_data *data = (struct dae_data*)x->user_data; + + if (streq(t, "node")) + data->node_level--; + + if (data->state == PARSING_NODE) { + data->state = PARSING_START; + } +} + +static void parse_floats(const char *d, int dl, float **floats, int nfloats) +{ + float val; + int i; + + assert(nfloats > 0); + *floats = calloc(nfloats, sizeof(float)); + + const char *p = d; + + for (i = 0; i < nfloats; i++) { + sscanf(p, "%f", &val); + (*floats)[i] = val; + + while (p < d+dl) { + if (*(p++) == ' ') + break; + } + } + + assert(nfloats == i); +} + +static void parse_weights(struct dae_data *data, const char *d, int dl) +{ + assert(data->counter != -1); + float *weights; + parse_floats(d, dl, &weights, data->counter); + struct model *model = data->model; + struct pose *pose = &model->poses[model->nposes]; + assert(pose); + pose->nweights = data->counter; + pose->weights = weights; + data->state = PARSING_POSE; + data->counter = -1; +} + +static void parse_vertices(struct dae_data *data, float **verts, + const char *d, int dl) +{ + assert(data->counter != -1); + parse_floats(d, dl, verts, data->counter); + +} + +static void parse_joint_matrix(struct dae_data *data, const char *d, + int dl) +{ + assert(data->model->nposes); + + struct model *model = data->model; + struct pose *pose = &model->poses[model->nposes - 1]; + assert(pose); + + struct joint *joint = &pose->joints[pose->njoints]; + assert(joint); + + struct node *node = new_node(&joint->node_id); + assert(node); + assert((int64_t)joint->node_id.uuid != -1); + assert(&joint->node_id == &pose->joints[pose->njoints].node_id); + + parse_joint(d, pose->njoints, joint); + print_id(&joint->node_id, 1); + node_set_label(node, data->current_name); + joint->children_ids[0] = data->node_level; + pose->njoints++; + data->state = PARSING_POSE; +} + +static void dae_tagbody(struct xmlparser *x, const char *d, size_t dl) +{ + static int count = 0; + struct dae_data *data = (struct dae_data*)x->user_data; + + if (data->state == PARSING_JOINT_MATRIX) { + parse_joint_matrix(data, d, dl); + } + else if (data->state == PARSING_WEIGHTS) { + parse_weights(data, d, dl); + } + else if (data->state == PARSING_NORMALS) { + } +} + +static int dae_getc(struct xmlparser *x) +{ + struct dae_data *data = (struct dae_data*)x->user_data; + return fgetc(data->dae_file); +} + +void dae_attr(struct xmlparser *x, const char *t, size_t tl, + const char *a, size_t al, const char *v, size_t vl) +{ + struct dae_data *data = (struct dae_data*)x->user_data; + + if (data->state == PARSING_NODE + && streq(a, "id") + && streq(v, "Armature")) { + + struct model *model = data->model; + struct pose *pose = &model->poses[model->nposes++]; + data->state = PARSING_POSE; + init_pose(pose); + } + else if (data->state == PARSING_FLOAT_ARRAY && streq(a, "id")) { + if (contains(v, "skin-weights-array")) { + data->state = PARSING_WEIGHTS; + } + else if (contains(v, "positions-array")) { + data->state = PARSING_POSITIONS; + } + else if (contains(v, "normals-array")) { + data->state = PARSING_NORMALS; + } + else if (contains(v, "colors")) { + data->state = PARSING_COLORS; + } + } + else if (data->state == PARSING_WEIGHTS + && streq(a, "count")) { + data->counter = atoi(v); + } + else if (data->state == PARSING_NODE + && streq(a, "name")) { + strncpy(data->current_name, v, sizeof(data->current_name)); + } + else if (data->state == PARSING_NODE + && streq(a, "type") + && streq(v, "JOINT")) { + + data->state = PARSING_JOINT; + } +} + +static void process_joint_children(struct joint *joints, int njoints) +{ + struct joint *joint, *j2; + for (int i = 0; i < njoints; i++) { + joint = &joints[i]; + + // node level is stored in here on the first parser pass + int level = joint->children_ids[0]; + + for (int j = i+1; j < njoints; j++) { + j2 = &joints[j]; + if (j2->children_ids[0] == level + 1) { + /* printf("%s(%d) assigning child %s(%d)\n", */ + /* joint->name, level, j2->name, j2->children[0]); */ + + assert(joint->n_children_ids+1 < MAX_JOINT_CHILDREN); + joint->children_ids[joint->n_children_ids++] = j; + } + else if (j2->children_ids[0] <= level) + break; + } + } +} + +void load_model(const char *filename, struct model *model, + struct make_geometry *geom) +{ + struct xmlparser x = {0}; + struct dae_data data = { + .node_level = 0, + .state = PARSING_START, + .model = model, + .geom = geom, + .counter = -1 + }; + + data.dae_file = fopen(filename, "rb"); + if (data.dae_file == NULL) + exit(1); + + x.user_data = &data; + x.xmltagstart = dae_tag_start; + x.xmltagend = dae_tag_end; + x.xmlattr = dae_attr; + x.xmldata = dae_tagbody; + /* x.xmlattrend = dae_attr_end; */ + x.getnext = dae_getc; + + xml_parse(&x); + + for (int i = 0; i < model->nposes; i++) { + struct pose *pose = &model->poses[i]; + process_joint_children(pose->joints, pose->njoints); + } + + + fclose(data.dae_file); +} diff --git a/src/dae.h b/src/dae.h @@ -0,0 +1,11 @@ + +#ifndef DAE_H +#define DAE_H + +#include "geometry.h" +#include "model.h" + +void load_model(const char *filename, struct model *model, + struct make_geometry *geom); + +#endif /* DAE_H */ diff --git a/src/model.c b/src/model.c @@ -20,6 +20,10 @@ struct model *init_model(struct model *model) { init_id(&model->geom_id); model->shading = SHADING_VERT_COLOR; model->texture = 0; + model->nposes = 0; + for (u16 i = 0; i < ARRAY_SIZE(model->poses); i++) { + init_pose(model->poses); + } return model; } diff --git a/src/model.h b/src/model.h @@ -7,11 +7,13 @@ #include "shader.h" #include "geometry.h" #include "common.h" +#include "animation.h" #include "static_resources.h" #define DEF_DYNAMIC_MODELS 128 #define MAX_STATIC_MODELS 128 #define MAX_DYNAMIC_MODELS 2048 +#define MAX_POSES 2 typedef struct resource_id model_id; @@ -25,6 +27,8 @@ struct model { /* geometry_id geom_id; */ geometry_id geom_id; enum shading shading; + struct pose poses[MAX_POSES]; // TODO: separate animated_model buffer? + int nposes; u32 texture; }; diff --git a/test/test_animation.c b/test/test_animation.c @@ -1,48 +0,0 @@ - -#include "animation.h" -#include "util.h" -#include "debug.h" -#include <assert.h> -#include <stdio.h> - - -int main(int argc, char *argv[]) -{ - struct pose *pose; - struct joint *joint; - struct node *node; - struct pose poses[4]; - int nposes; - - init_node_manager(); - - for (int i = 0; i < (int)ARRAY_SIZE(poses); i++) { - init_pose(&poses[i]); - } - - load_poses("data/models/pirate-officer.dae", poses, &nposes); - assert(nposes == 1); - pose = &poses[0]; - - debug("pose->nweights %d\n", pose->nweights); - assert(pose->nweights == 389); - assert(approxeq(pose->weights[0], 0.05213558)); - debug("pose last weight %f\n", pose->weights[388]); - assert(approxeq(pose->weights[388], 0.01394611)); - - assert(pose->njoints == 11); - - joint = &pose->joints[0]; - node = get_node(&joint->node_id); - assert(node); - assert(approxeq(node->mat[0], 0.999897)); - - joint = &pose->joints[1]; - node = get_node(&joint->node_id); - assert(node); - assert(approxeq(node->mat[1], -0.01434187)); - - assert(pose->joints[0].n_children_ids == 3); - - return 0; -} diff --git a/test/test_dae.c b/test/test_dae.c @@ -0,0 +1,48 @@ + +#include "animation.h" +#include "util.h" +#include "debug.h" +#include <assert.h> +#include <stdio.h> + +#include "dae.h" + +int main(int argc, char *argv[]) +{ + struct pose *pose; + struct joint *joint; + struct node *node; + struct model model; + struct make_geometry geom; + int nposes; + + init_node_manager(); + init_model(&model); + init_make_geometry(&geom); + + load_model("data/models/pirate-officer.dae", &model, &geom); + assert(model.nposes == 1); + pose = &model.poses[0]; + + debug("pose->nweights %d\n", pose->nweights); + assert(pose->nweights == 389); + assert(approxeq(pose->weights[0], 0.05213558)); + debug("pose last weight %f\n", pose->weights[388]); + assert(approxeq(pose->weights[388], 0.01394611)); + + assert(pose->njoints == 11); + + joint = &pose->joints[0]; + node = get_node(&joint->node_id); + assert(node); + assert(approxeq(node->mat[0], 0.999897)); + + joint = &pose->joints[1]; + node = get_node(&joint->node_id); + assert(node); + assert(approxeq(node->mat[1], -0.01434187)); + + assert(pose->joints[0].n_children_ids == 3); + + return 0; +} diff --git a/tools/compile-model.c b/tools/compile-model.c @@ -3,234 +3,13 @@ #include <stdlib.h> #include <assert.h> -#include "xml.h" -#include "util.h" -#include "animation.h" +#include "dae.h" -enum dae_state { - PARSING_START, - PARSING_NODE, - PARSING_FLOAT_ARRAY, - PARSING_POSE, - PARSING_JOINT, - PARSING_JOINT_MATRIX, - PARSING_WEIGHTS, -}; - -struct dae_data { - int node_level; - int state; - FILE *dae_file; - struct pose *poses; - int *nposes; - float *weights; - int nweights; - char current_name[JOINT_LABEL_SIZE]; -}; - - -static void parse_joint(const char *t, int id, struct joint *joint) -{ - struct node *node = get_node(&joint->node_id); - assert(node); - - float *m = node->mat; - joint->id = id; - /* printf(" parsing joint %d: %s\n", id, t); */ - - sscanf(t, "%f %f %f %f %f %f %f %f %f %f %f %f %f %f %f %f", - &m[0], &m[1], &m[2], &m[3], - &m[4], &m[5], &m[6], &m[7], - &m[8], &m[9], &m[10], &m[11], - &m[12], &m[13], &m[14], &m[15] - ); -} - - -static void dae_tag_start(struct xmlparser *x, const char *t, size_t tl) -{ - struct dae_data *data = (struct dae_data*)x->user_data; - - if (streq(t, "node")) { - data->state = PARSING_NODE; - data->node_level++; - } - else if (streq(t, "float_array")) { - data->state = PARSING_FLOAT_ARRAY; - } - else if (data->state == PARSING_JOINT && streq(t, "matrix")) - data->state = PARSING_JOINT_MATRIX; - else - return; -} - -static void dae_tag_end(struct xmlparser *x, const char *t, size_t tl, int what) -{ - struct dae_data *data = (struct dae_data*)x->user_data; - - if (streq(t, "node")) - data->node_level--; - - if (data->state == PARSING_NODE) { - data->state = PARSING_START; - } -} - -static void dae_tagbody(struct xmlparser *x, const char *d, size_t dl) -{ - static int count = 0; - struct dae_data *data = (struct dae_data*)x->user_data; - struct pose *pose; - - if (data->state == PARSING_JOINT_MATRIX) { - assert(*data->nposes); - - pose = &data->poses[*data->nposes - 1]; - assert(pose); - - struct joint *joint = &pose->joints[pose->njoints]; - assert(joint); - - struct node *node = new_node(&joint->node_id); - assert(node); - assert((int64_t)joint->node_id.uuid != -1); - assert(&joint->node_id == &pose->joints[pose->njoints].node_id); - - parse_joint(d, pose->njoints, joint); - print_id(&joint->node_id, 1); - node_set_label(node, data->current_name); - joint->children_ids[0] = data->node_level; - pose->njoints++; - data->state = PARSING_POSE; - } - else if (data->state == PARSING_WEIGHTS) { - pose = &data->poses[*data->nposes]; - assert(data->nweights > 0); - data->weights = calloc(data->nweights, sizeof(float)); - - const char *p = d; - float val; - int i; - - for (i = 0; i < data->nweights; i++) { - sscanf(p, "%f", &val); - data->weights[i] = val; - - while (p < d+dl) { - if (*(p++) == ' ') - break; - } - } - - assert(data->nweights == i); - - pose->nweights = data->nweights; - pose->weights = data->weights; - - data->state = PARSING_POSE; - } -} - -static int dae_getc(struct xmlparser *x) -{ - struct dae_data *data = (struct dae_data*)x->user_data; - return fgetc(data->dae_file); -} - -void dae_attr(struct xmlparser *x, const char *t, size_t tl, - const char *a, size_t al, const char *v, size_t vl) +static void save_mdl(struct model *model, struct make_geometry *geom) { - struct dae_data *data = (struct dae_data*)x->user_data; - - if (data->state == PARSING_NODE - && streq(a, "id") - && streq(v, "Armature")) { - - struct pose *pose = &data->poses[(*data->nposes)++]; - data->state = PARSING_POSE; - init_pose(pose); - } - else if (data->state == PARSING_FLOAT_ARRAY - && streq(a, "id") - && contains(v, "skin-weights-array")) { - data->state = PARSING_WEIGHTS; - } - else if (data->state == PARSING_WEIGHTS - && streq(a, "count")) { - data->nweights = atoi(v); - } - else if (data->state == PARSING_NODE - && streq(a, "name")) { - strncpy(data->current_name, v, sizeof(data->current_name)); - } - else if (data->state == PARSING_NODE - && streq(a, "type") - && streq(v, "JOINT")) { - - data->state = PARSING_JOINT; - } -} - -static void process_joint_children(struct joint *joints, int njoints) -{ - struct joint *joint, *j2; - for (int i = 0; i < njoints; i++) { - joint = &joints[i]; - - // node level is stored in here on the first parser pass - int level = joint->children_ids[0]; - for (int j = i+1; j < njoints; j++) { - j2 = &joints[j]; - if (j2->children_ids[0] == level + 1) { - /* printf("%s(%d) assigning child %s(%d)\n", */ - /* joint->name, level, j2->name, j2->children[0]); */ - - assert(joint->n_children_ids+1 < MAX_JOINT_CHILDREN); - joint->children_ids[joint->n_children_ids++] = j; - } - else if (j2->children_ids[0] <= level) - break; - } - } } -void load_poses(const char *filename, struct pose *poses, int *nposes) -{ - *nposes = 0; - - struct xmlparser x = {0}; - struct dae_data data = { - .node_level = 0, - .state = PARSING_START, - .poses = poses, - .nposes = nposes, - .nweights = -1, - .weights = NULL - }; - - data.dae_file = fopen(filename, "rb"); - if (data.dae_file == NULL) - exit(1); - - x.user_data = &data; - x.xmltagstart = dae_tag_start; - x.xmltagend = dae_tag_end; - x.xmlattr = dae_attr; - x.xmldata = dae_tagbody; - /* x.xmlattrend = dae_attr_end; */ - x.getnext = dae_getc; - - xml_parse(&x); - - for (int i = 0; i < *nposes; i++) { - struct pose *pose = &poses[i]; - process_joint_children(pose->joints, pose->njoints); - } - - - fclose(data.dae_file); -} void usage() { @@ -243,17 +22,26 @@ int main(int argc, char *argv[]) if (argc != 3) usage(); + init_node_manager(); + init_geometry_manager(); + init_model_manager(); + const char *filename = argv[1]; const char *outfile = argv[2]; + struct model model; + struct make_geometry geom; + + init_model(&model); + init_make_geometry(&geom); + int nposes; - static struct pose poses[5]; - load_poses(outfile, poses, &nposes); + load_model(filename, &model, &geom); FILE *out = fopen(outfile, "wb"); fprintf(out, "hello, world\n"); - fclose(out); + return 0; }