diff --git a/src/half_edge.h b/src/half_edge.h index d9668c3..70ca239 100644 --- a/src/half_edge.h +++ b/src/half_edge.h @@ -84,21 +84,63 @@ } +typedef struct obj_items obj_items; +/** + * 2d array which can hold the vertices + * as they are in the .obj file, + */ typedef double** VERTICES; typedef struct FACES FACES; +/** + * 2d array which can hold the vertices texture + * coordinates as they are in the .obj file, + */ typedef double** V_TEXTURES; typedef struct HE_edge HE_edge; typedef struct HE_vert HE_vert; +typedef struct HE_vert_acc HE_vert_acc; typedef struct HE_face HE_face; typedef struct HE_obj HE_obj; typedef struct color color; -struct FACES { - uint32_t **v; - uint32_t **vt; +/** + * This will hold all supported items, + * such as "v" (for vertice), "f" (for face) + * or "vt" (for vertex texture) as they are + * in the .obj file, not converted to anything. + */ +struct obj_items { + /** + * Raw vertices array. + */ + VERTICES v; + /** + * Raw faces array. + */ + FACES *f; + /** + * Raw texture coordinates array. + */ + V_TEXTURES vt; }; +/** + * Represents the raw faces as they are in the .obj + * file with their references to vertices etc. + */ +struct FACES { + /** + * References to the vertices which + * will form the face. + */ + uint32_t **v; + /** + * Reference to the texture coordinates + * which will form the polygon texture. + */ + uint32_t **vt; +}; /** * Represents a half-edge. @@ -134,14 +176,24 @@ struct HE_vert { * to the coordinates of the vertex. */ vector *vec; - /** * One of the half-edges emanating from the vertex. * It is made sure that this is never a border edge, * unless there are only border edges. */ HE_edge *edge; + /** + * Color of the vertex. + */ + color *col; + /** + * The acceleration structure, used to speed up + * assembling the half-edge structures. + */ + HE_vert_acc *acc; +}; +struct HE_vert_acc { /** * Acceleration structure which saves all potential * pair edges that point TO this vertex. It is used @@ -152,28 +204,20 @@ struct HE_vert { * don't qualify for pairs anyway). */ HE_edge **edge_array; - /** * Similar as the edge_array acceleration structure, * except that it is used for connecting the * dummy edges. */ HE_edge **dummys; - /** * Element count of the edge_array. */ uint32_t eac; - /** * Element count of dummys. */ uint32_t dc; - - /** - * Color of the vertex. - */ - color *col; }; /** @@ -216,6 +260,14 @@ struct HE_obj { * Count of faces. */ uint32_t fc; + /** + * Count of the vertex texture coordinates. + */ + uint32_t vtc; + /** + * Count of dummy edges. + */ + uint32_t dec; }; /** diff --git a/src/half_edge_AS.c b/src/half_edge_AS.c index 60626d6..f8b87c2 100644 --- a/src/half_edge_AS.c +++ b/src/half_edge_AS.c @@ -34,27 +34,22 @@ #include +/* + * static function declaration + */ static bool assemble_obj_arrays(char const * const obj_string, - VERTICES *obj_v_out, - V_TEXTURES *obj_vt_out, - FACES **obj_f_out, - uint32_t *vc_out, - uint32_t *fc_out, - uint32_t *ec_out, - uint32_t *vtc_out); -static void assemble_HE_stage1(VERTICES obj_v, - HE_vert *vertices, - uint32_t *vc, - uint32_t *fc); -static void assemble_HE_stage2(FACES *obj_f, - HE_vert *vertices, - HE_face *faces, - HE_edge *edges, - uint32_t *fc, - uint32_t *ec); -static void assemble_HE_stage3(HE_edge *edges, - uint32_t *ec, - uint32_t *dec); + obj_items *raw_obj, + HE_obj *he_obj); +static void assemble_HE_stage1(obj_items const * const raw_obj, + HE_obj *he_obj); +static void assemble_HE_stage2(obj_items const * const raw_obj, + HE_obj *he_obj); +static void assemble_HE_stage3(HE_obj *he_obj); +static void delete_accel_struct(HE_obj *he_obj); +static void delete_raw_object(obj_items *raw_obj, + uint32_t fc, + uint32_t vc, + uint32_t vt); /** @@ -66,30 +61,16 @@ static void assemble_HE_stage3(HE_edge *edges, * once. * * @param obj_string the string that is in obj format - * @param obj_v_out where to save the vertices, the rear - * dimension will always have 3 elements for x,y,z [out] - * @param obj_vt_out where to save the texture coordinates [out] - * @param obj_f_out where to save the faces, the rear dimension - * will be terminated with a "0" [out] - * @param vc_out pointer to save vertice count [out] - * @param fc_out pointer to save face count [out] - * @param ec_out pointer to save edge count [out] - * @param vtc_out pointer to save texture coordinates count [out] + * @param raw_obj contains arrays of the items as they are in the .obj + * file; members v, f and vt are set [out] + * @param he_obj the half-edge object containing array-pointers + * to all the HE_* structures; members ec, fc, vc and vtc are set [out] * @return true/false for success/failure */ static bool assemble_obj_arrays(char const * const obj_string, - VERTICES *obj_v_out, - V_TEXTURES *obj_vt_out, - FACES **obj_f_out, - uint32_t *vc_out, - uint32_t *fc_out, - uint32_t *ec_out, - uint32_t *vtc_out) + obj_items *raw_obj, + HE_obj *he_obj) { - uint32_t vc = 0, - fc = 0, - ec = 0, - vtc = 0; char *string; /* for strtok_r */ @@ -98,13 +79,15 @@ static bool assemble_obj_arrays(char const * const obj_string, *str_ptr_slash = NULL, *str_tmp_ptr = NULL; + /* these will be assigned later to the out structs */ + uint32_t vc = 0, fc = 0, ec = 0, vtc = 0; VERTICES obj_v = NULL; FACES *obj_f = malloc(sizeof(*obj_f)); - uint32_t **obj_f_v = NULL; - uint32_t **obj_f_vt = NULL; + uint32_t **obj_f_v = NULL; /* tmp v member of obj_f */ + uint32_t **obj_f_vt = NULL; /* tmp vt member of obj_f */ V_TEXTURES obj_vt = NULL; - if (!obj_string || !obj_v_out || !obj_f_out) + if (!obj_string || !raw_obj) return false; /* avoid side effects */ @@ -208,16 +191,16 @@ static bool assemble_obj_arrays(char const * const obj_string, str_tmp_ptr = strtok_r(NULL, "\n", &str_ptr_newline); } - /* set all out-variables */ - *obj_v_out = obj_v; - *obj_vt_out = obj_vt; + /* assign the out variables */ + he_obj->ec = ec; + he_obj->fc = fc; + he_obj->vc = vc; + he_obj->vtc = vtc; + raw_obj->v = obj_v; obj_f->v = obj_f_v; obj_f->vt = obj_f_vt; - *obj_f_out = obj_f; - *vc_out = vc; - *fc_out = fc; - *ec_out = ec; - *vtc_out = vtc; + raw_obj->f = obj_f; + raw_obj->vt = obj_vt; /* cleanup */ free(string); @@ -233,49 +216,51 @@ static bool assemble_obj_arrays(char const * const obj_string, * NULL. This function isn't really modular, but makes * reading parse_obj() a bit less painful. * - * @param obj_v the vertices in the raw form after they are - * parsed from the obj file - * @param vertices pointer the actual half-edge vertices - * @param vc pointer to the vertice count - * @param fc pointer to the face count + * @param raw_obj contains arrays of the items as they are in the .obj + * file + * @param he_obj the half-edge object containing array-pointers + * to all the HE_* structures; member vertices is set [out] */ -static void assemble_HE_stage1(VERTICES obj_v, - HE_vert *vertices, - uint32_t *vc, - uint32_t *fc) +static void assemble_HE_stage1(obj_items const * const raw_obj, + HE_obj *he_obj) { + uint32_t vc = 0; uint8_t const xpos = 0; uint8_t const ypos = 1; uint8_t const zpos = 2; int8_t default_color = -1; + HE_vert *vertices = he_obj->vertices; - *vc = 0; - - while (obj_v[*vc]) { + while (raw_obj->v[vc]) { vector *tmp_vec; tmp_vec = malloc(sizeof(vector)); CHECK_PTR_VAL(tmp_vec); - tmp_vec->x = obj_v[*vc][xpos]; - tmp_vec->y = obj_v[*vc][ypos]; - tmp_vec->z = obj_v[*vc][zpos]; + tmp_vec->x = raw_obj->v[vc][xpos]; + tmp_vec->y = raw_obj->v[vc][ypos]; + tmp_vec->z = raw_obj->v[vc][zpos]; - vertices[*vc].vec = tmp_vec; + vertices[vc].vec = tmp_vec; /* set unused/unknown values to NULL */ - vertices[*vc].edge = NULL; - vertices[*vc].edge_array = NULL; - vertices[*vc].eac = 0; - vertices[*vc].dc = 0; + vertices[vc].edge = NULL; - vertices[*vc].col = malloc(sizeof(color)); - vertices[*vc].col->red = default_color; - vertices[*vc].col->green = default_color; - vertices[*vc].col->blue = default_color; + vertices[vc].col = malloc(sizeof(color)); + vertices[vc].col->red = default_color; + vertices[vc].col->green = default_color; + vertices[vc].col->blue = default_color; - (*vc)++; + /* set acc structure */ + vertices[vc].acc = malloc(sizeof(HE_vert_acc)); + vertices[vc].acc->edge_array = NULL; + vertices[vc].acc->eac = 0; + vertices[vc].acc->dc = 0; + + vc++; } + + he_obj->vertices = vertices; } /** @@ -287,72 +272,78 @@ static void assemble_HE_stage1(VERTICES obj_v, * This function isn't really modular, but makes * reading parse_obj() a bit less painful. * - * @param obj_f the raw faces as they are after parsing the obj file - * @param vertices the half-edge vertices - * @param faces the half-edge faces - * @param edges the half-edge edges - * @param fc the count of half-edge faces - * @param ec the count of half-edge edges + * @param raw_obj contains arrays of the items as they are in the .obj + * file + * @param he_obj the half-edge object containing array-pointers + * to all the HE_* structures; member vertices, edges + * and faces are modified [out] */ -static void assemble_HE_stage2(FACES *obj_f, - HE_vert *vertices, - HE_face *faces, - HE_edge *edges, - uint32_t *fc, - uint32_t *ec) +static void assemble_HE_stage2(obj_items const * const raw_obj, + HE_obj *he_obj) { - *ec = 0; + HE_vert *vertices = he_obj->vertices; + HE_edge *edges = he_obj->edges; + HE_face *faces = he_obj->faces; + FACES *obj_f = raw_obj->f; + + uint32_t ec = 0, + fc = he_obj->fc; + /* create HE_edges and real HE_faces */ - for (uint32_t i = 0; i < *fc; i++) { /* for all faces */ + for (uint32_t i = 0; i < fc; i++) { /* for all faces */ uint32_t j = 0; /* for all vertices of the face */ while (obj_f->v[i][j]) { uint32_t fv_arr_id = obj_f->v[i][j] - 1; /* fv_id starts at 1 */ - edges[*ec].vert = &(vertices[fv_arr_id]); - edges[*ec].face = &(faces[i]); - edges[*ec].pair = NULL; /* preliminary */ - vertices[fv_arr_id].edge = &(edges[*ec]); /* last one wins */ - vertices[fv_arr_id].dummys = NULL; /* preliminary */ + edges[ec].vert = &(vertices[fv_arr_id]); + edges[ec].face = &(faces[i]); + edges[ec].pair = NULL; /* preliminary */ + vertices[fv_arr_id].edge = &(edges[ec]); /* last one wins */ + vertices[fv_arr_id].acc->dummys = NULL; /* preliminary */ /* Skip j == 0 here, so we don't underrun the arrays, * since we always look one edge back. The first edge * element is taken care of below as well. */ if (j > 0) { - uint32_t *eac = &(edges[*ec].vert->eac); + uint32_t *eac = &(edges[ec].vert->acc->eac); /* connect previous edge to current edge */ - edges[*ec - 1].next = &(edges[*ec]); + edges[ec - 1].next = &(edges[ec]); /* Acceleration struct: * add previous edge to edge_array of current vertice */ - REALLOC(edges[*ec].vert->edge_array, + REALLOC(edges[ec].vert->acc->edge_array, sizeof(HE_edge*) * (*eac + 1)); - edges[*ec].vert->edge_array[*eac] = &(edges[*ec - 1]); + edges[ec].vert->acc->edge_array[*eac] = &(edges[ec - 1]); (*eac)++; if (!obj_f->v[i][j + 1]) { /* no vertice left */ uint32_t *eac; /* connect last edge to first edge */ - edges[*ec].next = &(edges[*ec - j]); - eac = &(edges[*ec].next->vert->eac); + edges[ec].next = &(edges[ec - j]); + eac = &(edges[ec].next->vert->acc->eac); /* Acceleration struct: * add last edge to edge_array element of first vertice */ - REALLOC(edges[*ec].next->vert->edge_array, + REALLOC(edges[ec].next->vert->acc->edge_array, sizeof(HE_edge*) * (*eac + 1)); - edges[*ec].next->vert->edge_array[*eac] = &(edges[*ec]); + edges[ec].next->vert->acc->edge_array[*eac] = &(edges[ec]); (*eac)++; } } - (*ec)++; + ec++; j++; } - faces[i].edge = &(edges[*ec - 1]); /* "last" edge */ + faces[i].edge = &(edges[ec - 1]); /* "last" edge */ } + + he_obj->vertices = vertices; + he_obj->edges = edges; + he_obj->faces = faces; } /** @@ -361,25 +352,26 @@ static void assemble_HE_stage2(FACES *obj_f, * possibility of border-edges, where we have to set up * dummy edges and connect them properly. * - * @param edges the half-edge edges - * @param ec the half-edge edges count - * @param dec the dummy edges count + * @param he_obj the half-edge object containing array-pointers; + * member dec is set and edges is modified [out] */ -static void assemble_HE_stage3(HE_edge *edges, - uint32_t *ec, - uint32_t *dec) +static void assemble_HE_stage3(HE_obj *he_obj) { + HE_edge *edges = he_obj->edges; + uint32_t ec = he_obj->ec; + uint32_t dec = 0; + /* find pairs */ - for (uint32_t i = 0; i < *ec; i++) { /* for all edges */ - uint32_t eac = edges[i].vert->eac; + for (uint32_t i = 0; i < ec; i++) { /* for all edges */ + uint32_t eac = edges[i].vert->acc->eac; bool pair_found = false; for (uint32_t j = 0; j < eac; j++) { /* for all potential pairs */ - if (edges[i].vert->edge_array[j] && + if (edges[i].vert->acc->edge_array[j] && (edges[i].next->vert == - edges[i].vert->edge_array[j]->vert)) { - edges[i].pair = edges[i].vert->edge_array[j]; - edges[i].vert->edge_array[j] = NULL; + edges[i].vert->acc->edge_array[j]->vert)) { + edges[i].pair = edges[i].vert->acc->edge_array[j]; + edges[i].vert->acc->edge_array[j] = NULL; pair_found = true; break; @@ -388,40 +380,43 @@ static void assemble_HE_stage3(HE_edge *edges, /* create dummy pair edge if we have a border edge */ if (!pair_found) { - uint32_t *vert_dc = &(edges[i].next->vert->dc); + uint32_t *vert_dc = &(edges[i].next->vert->acc->dc); - REALLOC(edges[i].next->vert->dummys, + REALLOC(edges[i].next->vert->acc->dummys, sizeof(HE_edge*) * (*vert_dc + 1)); /* NULL-face indicates border-edge */ - edges[*ec + *dec].face = NULL; + edges[ec + dec].face = NULL; /* we don't know this one yet */ - edges[*ec + *dec].next = NULL; + edges[ec + dec].next = NULL; /* set both pairs */ - edges[*ec + *dec].pair = &(edges[i]); - edges[i].pair = &(edges[*ec + *dec]); + edges[ec + dec].pair = &(edges[i]); + edges[i].pair = &(edges[ec + dec]); /* set vertex */ - edges[*ec + *dec].vert = edges[i].next->vert; + edges[ec + dec].vert = edges[i].next->vert; /* add the dummy edge to the dummys array of the vertex */ - edges[*ec + *dec].vert->dummys[*vert_dc] = &(edges[*ec + *dec]); + edges[ec + dec].vert->acc->dummys[*vert_dc] = &(edges[ec + dec]); (*vert_dc)++; - (*dec)++; + dec++; } } /* now we have to connect the dummy edges together */ - for (uint32_t i = 0; i < *dec; i++) { /* for all dummy edges */ + for (uint32_t i = 0; i < dec; i++) { /* for all dummy edges */ /* vertex the dummy edge points to */ - HE_vert *vert = edges[*ec + i].pair->vert; + HE_vert *vert = edges[ec + i].pair->vert; /* iterate over the dummy array */ - for (uint32_t j = 0; j < vert->dc; j++) { - if (vert == vert->dummys[j]->vert) - edges[*ec + i].next = vert->dummys[j]; + for (uint32_t j = 0; j < vert->acc->dc; j++) { + if (vert == vert->acc->dummys[j]->vert) + edges[ec + i].next = vert->acc->dummys[j]; j++; } } + + he_obj->edges = edges; + he_obj->dec = dec; } /** @@ -434,20 +429,10 @@ static void assemble_HE_stage3(HE_edge *edges, */ HE_obj *parse_obj(char const * const obj_string) { - uint32_t vc = 0, /* vertices count */ - fc = 0, /* face count */ - ec = 0, /* edge count */ - dec = 0, /* dummy edge count */ - vtc = 0; char *string = NULL, *str_ptr; - HE_vert *vertices = NULL; - HE_edge *edges = NULL; - HE_face *faces = NULL; - HE_obj *obj = NULL; - FACES *obj_f = NULL; - VERTICES obj_v = NULL; - V_TEXTURES obj_vt = NULL; + HE_obj *he_obj = NULL; + obj_items raw_obj; if (!obj_string || !*obj_string) return NULL; @@ -456,47 +441,81 @@ HE_obj *parse_obj(char const * const obj_string) strcpy(string, obj_string); str_ptr = string; - if (!assemble_obj_arrays(string, &obj_v, &obj_vt, &obj_f, - &vc, &fc, &ec, &vtc)) + /* + * allocation for he_obj + */ + he_obj = (HE_obj*) malloc(sizeof(HE_obj)); + CHECK_PTR_VAL(he_obj); + + /* + * assemble pseudo-object, also sets vc, fc, ec + */ + if (!assemble_obj_arrays(string, &raw_obj, he_obj)) return NULL; - vertices = malloc(sizeof(HE_vert) * - (vc + 1)); - CHECK_PTR_VAL(vertices); - faces = (HE_face*) malloc(sizeof(HE_face) * fc); - CHECK_PTR_VAL(faces); + /* + * he_obj member allocation + */ + he_obj->vertices = malloc(sizeof(HE_vert) * + (he_obj->vc + 1)); + CHECK_PTR_VAL(he_obj->vertices); + he_obj->faces = (HE_face*) malloc(sizeof(HE_face) * he_obj->fc); + CHECK_PTR_VAL(he_obj->faces); /* hold enough space for possible dummy edges */ - edges = (HE_edge*) malloc(sizeof(HE_edge) * ec * 2); - CHECK_PTR_VAL(edges); + he_obj->edges = (HE_edge*) malloc(sizeof(HE_edge) * he_obj->ec * 2); + CHECK_PTR_VAL(he_obj->edges); - assemble_HE_stage1(obj_v, vertices, &vc, &fc); - assemble_HE_stage2(obj_f, vertices, faces, edges, &fc, &ec); - assemble_HE_stage3(edges, &ec, &dec); - - obj = (HE_obj*) malloc(sizeof(HE_obj)); - CHECK_PTR_VAL(obj); - obj->edges = edges; - obj->vertices = vertices; - obj->faces = faces; - obj->ec = ec; - obj->vc = vc; - obj->fc = fc; + /* + * run the stages of assemblance + */ + assemble_HE_stage1(&raw_obj, he_obj); + assemble_HE_stage2(&raw_obj, he_obj); + assemble_HE_stage3(he_obj); /* cleanup */ - for (uint32_t i = 0; i < fc; i++) { - free(obj_f->v[i]); - free(obj_f->vt[i]); - } - free(obj_f->v); - free(obj_f->vt); - free(obj_f); - for (uint32_t i = 0; i < vc; i++) { - free(vertices[i].dummys); - free(vertices[i].edge_array); - free(obj_v[i]); - } - free(obj_v); + delete_raw_object(&raw_obj, he_obj->fc, he_obj->vc, he_obj->vtc); + delete_accel_struct(he_obj); free(string); - return obj; + return he_obj; +} + +/** + * Delete the acceleration structure of + * HE_vert. + */ +static void delete_accel_struct(HE_obj *he_obj) +{ + for (uint32_t i = 0; i < he_obj->vc; i++) { + free(he_obj->vertices[i].acc->dummys); + free(he_obj->vertices[i].acc->edge_array); + free(he_obj->vertices[i].acc); + } +} + +/** + * Delete the raw obj pseudo struct which is only + * used for assembling the HE_obj. + */ +static void delete_raw_object(obj_items *raw_obj, + uint32_t fc, + uint32_t vc, + uint32_t vtc) +{ + if (!raw_obj) + return; + + for (uint32_t i = 0; i < vtc; i++) + free(raw_obj->vt[i]); + for (uint32_t i = 0; i < vc; i++) + free(raw_obj->v[i]); + for (uint32_t i = 0; i < fc; i++) { + free(raw_obj->f->v[i]); + free(raw_obj->f->vt[i]); + } + free(raw_obj->f->v); + free(raw_obj->f->vt); + free(raw_obj->v); + free(raw_obj->vt); + free(raw_obj->f); }