vx_object_t * _vxo_box_private(vx_style_t * style, ...) { // Make sure the static geometry is initialized safely, correctly, and quickly if (vertex_points == NULL) { pthread_mutex_lock(&vx_convenience_mutex); if (vertex_points == NULL) { vxo_box_init(); vx_global_register_destroy(vxo_box_destroy, NULL); } pthread_mutex_unlock(&vx_convenience_mutex); } vx_object_t * vc = vxo_chain_create(); va_list va; va_start(va, style); for (vx_style_t * sty = style; sty != NULL; sty = va_arg(va, vx_style_t *)) { switch(sty->type) { case VXO_POINTS_STYLE: vxo_chain_add(vc, vxo_points(vertex_points, NVERTS, sty)); break; case VXO_LINES_STYLE: vxo_chain_add(vc, vxo_lines_indexed(vertex_points, NVERTS, line_indices, GL_LINES, sty)); break; case VXO_MESH_STYLE: // XXX always pass normals? vxo_chain_add(vc, vxo_mesh(tri_points, N_TRI_VERT, tri_normals, GL_TRIANGLES, sty)); break; } } va_end(va); return vc; }
vx_object_t * vxo_objmtl(const char * obj_filename) { int texture_flag = 0; FILE * fp_obj = fopen(obj_filename, "r"); if (fp_obj == NULL) return NULL; #define LNSZ 1024 char line_buffer[LNSZ]; // Store 3D vertices by value zarray_t * vertices = zarray_create(sizeof(float)*3); zarray_t * textures = zarray_create(sizeof(float)*3); zarray_t * normals = zarray_create(sizeof(float)*3); wav_group_t * cur_group = NULL; zarray_t * group_list = zarray_create(sizeof(wav_group_t*)); zhash_t * mtl_map = NULL; // created on reading mtllib entry //zhash_t * obj_map = zhash_create(sizeof(char*), sizeof(vx_object_t*), zhash_str_hash, zhash_str_equals); // Read in the entire file, save vertices, and indices for later processing. while (1) { int eof = fgets(line_buffer, LNSZ, fp_obj) == NULL; char * line = str_trim(line_buffer); // If possible, batch process the last group if (str_starts_with(line, "g ") || eof) { if (cur_group != NULL) { assert(cur_group->group_idx != NULL); zarray_add(group_list, &cur_group); cur_group = NULL; } } if (eof) break; if (str_starts_with(line, "#") || strlen(line) == 0 || !strcmp(line,"\r")) continue; if (str_starts_with(line, "g ")) { assert(mtl_map != NULL); char obj_name[LNSZ]; sscanf(line, "g %s", obj_name); cur_group = calloc(1, sizeof(wav_group_t)); cur_group->group_idx = zarray_create(sizeof(tri_idx_t)); } else if (str_starts_with(line, "v ")) { float vertex[3]; sscanf(line, "v %f %f %f", &vertex[0], &vertex[1], &vertex[2]); zarray_add(vertices, &vertex); } else if (str_starts_with(line, "vn ")) { float normal[3]; sscanf(line, "vn %f %f %f", &normal[0], &normal[1], &normal[2]); zarray_add(normals, &normal); } else if (str_starts_with(line, "vt ")) { texture_flag = 1; float texture[3]; sscanf(line, "vt %f %f %f", &texture[0], &texture[1], &texture[2]); zarray_add(textures, &texture); } else if (str_starts_with(line, "f ")) { tri_idx_t idxs; if (texture_flag) { sscanf(line, "f %d/%d/%d %d/%d/%d %d/%d/%d", &idxs.vIdxs[0], &idxs.tIdxs[0], &idxs.nIdxs[0], &idxs.vIdxs[1], &idxs.tIdxs[1], &idxs.nIdxs[1], &idxs.vIdxs[2], &idxs.tIdxs[2], &idxs.nIdxs[2]); } else { sscanf(line, "f %d//%d %d//%d %d//%d", &idxs.vIdxs[0], &idxs.nIdxs[0], &idxs.vIdxs[1], &idxs.nIdxs[1], &idxs.vIdxs[2], &idxs.nIdxs[2]); } zarray_add(cur_group->group_idx, &idxs); } else if (str_starts_with(line, "usemtl ")) { char *mname = calloc(1, sizeof(char)*1024); sscanf(line, "usemtl %s", mname); zhash_get(mtl_map, &mname, &cur_group->material); free(mname); } else if (str_starts_with(line, "s ")) { // No idea what to do with smoothing instructions } else if (str_starts_with(line, "mtllib ")) { char * cur_path = strdup(obj_filename); const char * dir_name = dirname(cur_path); char mtl_basename[LNSZ]; sscanf(line, "mtllib %s", mtl_basename); char mtl_filename[LNSZ]; sprintf(mtl_filename,"%s/%s", dir_name, mtl_basename); mtl_map = load_materials(mtl_filename); if (mtl_map == NULL) { zarray_destroy(vertices); zarray_destroy(normals); return NULL; // XXX cleanup! } free(cur_path); } else { printf("Did not parse: %s\n", line); for (int i = 0; i < strlen(line); i++) { printf("0x%x ", (int)line[i]); } printf("\n"); } } if (1) // useful to enable when compensating for model scale print_bounds(vertices); // Process the model sections in two passes -- first add all the // objects which are not transparent. Then render transparent // objects after vx_object_t * vchain = vxo_chain_create(); zarray_t * sorted_groups = zarray_create(sizeof(wav_group_t*)); for (int i = 0, sz = zarray_size(group_list); i < sz; i++) { wav_group_t * group = NULL; zarray_get(group_list, i, &group); // add to front if solid if (group->material.d == 1.0f) { zarray_insert(sorted_groups, 0, &group); } else { // add to back if transparent zarray_add(sorted_groups, &group); } } int total_triangles = 0; for (int i = 0, sz = zarray_size(sorted_groups); i < sz; i++) { wav_group_t * group = NULL; zarray_get(sorted_groups, i, &group); int ntri = zarray_size(group->group_idx); vx_resc_t * vert_resc = vx_resc_createf(ntri*9); vx_resc_t * norm_resc = vx_resc_createf(ntri*9); for (int j = 0; j < ntri; j++) { tri_idx_t idxs; zarray_get(group->group_idx, j, &idxs); for (int i = 0; i < 3; i++) { zarray_get(vertices, idxs.vIdxs[i]-1, &((float*)vert_resc->res)[9*j + i*3]); zarray_get(normals, idxs.nIdxs[i]-1, &((float*)norm_resc->res)[9*j + i*3]); } } vx_style_t * sty = vxo_mesh_style_fancy(group->material.Ka, group->material.Kd, group->material.Ks, group->material.d, group->material.Ns, group->material.illum); vxo_chain_add(vchain, vxo_mesh(vert_resc, ntri*3, norm_resc, GL_TRIANGLES, sty)); total_triangles += ntri; } //Cleanup: // 1. Materials, names are by reference, but materials are by value zhash_vmap_keys(mtl_map, free); zhash_destroy(mtl_map); // 2. Geometry zarray_destroy(vertices); // stored by value zarray_destroy(normals); // stored by value // 2b wav_group_t are stored by reference zarray_vmap(group_list, wav_group_destroy); zarray_destroy(group_list); zarray_destroy(sorted_groups); // duplicate list, so don't need to free fclose(fp_obj); return vchain; }