#include #include #include #include #include /* * format description: * http://www.quick3d.com/guide/guidec.html */ static void q3o_update_face_textures(G3DModel *model); static int q3o_read_mesh(FILE *f, G3DModel *model, guint32 n_textures); static int q3o_read_material(FILE *f, G3DModel *model, guint32 index, guint32 n_textures); static int q3o_read_texture(FILE *f, G3DModel *model); static int q3o_read_scene(FILE *f, G3DModel *model); static int q3o_read_eof(FILE *f); int plugin_load(const char *filename, G3DModel *model) { gchar signature[8], ver_min, ver_maj, id; guint32 nmeshes, nmats, ntexs, i; FILE *f; f = fopen(filename, "rb"); if(f == NULL) { g_warning("could not open '%s'", filename); return EXIT_FAILURE; } fread(signature, 1, 8, f); if(strncmp(signature, "quick3Ds", 8) && strncmp(signature, "quick3Do", 8)) { g_warning("file '%s' is not a Quick3D file", filename); fclose(f); return EXIT_FAILURE; } ver_maj = g3d_read_int8(f); ver_min = g3d_read_int8(f); #if DEBUG > 0 g_print("Q3O: version %c.%c\n", ver_maj, ver_min); #endif nmeshes = g3d_read_int32_le(f); nmats = g3d_read_int32_le(f); ntexs = g3d_read_int32_le(f); #if DEBUG > 0 g_print("Q3O: %d meshes, %d materials, %d textures\n", nmeshes, nmats, ntexs); #endif /* generate (emtpy) materials */ for(i = 0; i < nmats; i ++) { G3DMaterial *material = g3d_new_G3DMaterial(); model->materials = g_slist_append(model->materials, material); } while((id = g3d_read_int8(f)) != 0) { #if DEBUG > 0 g_print("Q3O: chunk type 0x%02x @ 0x%08lx\n", id, ftell(f) - 1); #endif switch(id) { case 'm': /* mesh */ for(i = 0; i < nmeshes; i ++) q3o_read_mesh(f, model, ntexs); break; case 'c': /* material */ for(i = 0; i < nmats; i ++) q3o_read_material(f, model, i, ntexs); break; case 't': /* texture */ for(i = 0; i < ntexs; i ++) q3o_read_texture(f, model); break; case 's': /* scene */ q3o_read_scene(f, model); break; case 'q': /* EOF signature? */ q3o_read_eof(f); break; default: g_warning("Q3O: unknown chunk type 0x%02x\n", id); fclose(f); return EXIT_SUCCESS; break; } } fclose(f); /* update texture images */ q3o_update_face_textures(model); return EXIT_SUCCESS; } char *plugin_description(void) { return g_strdup("import plugin for Quick3D objects\n"); } char **plugin_extensions(void) { return g_strsplit("q3o:q3s", ":", 0); } static void q3o_update_face_textures(G3DModel *model) { GSList *oitem, *fitem; G3DObject *object; G3DFace *face; oitem = model->objects; while(oitem) { object = (G3DObject *)oitem->data; fitem = object->faces; while(fitem) { face = (G3DFace *)fitem->data; face->tex_image = face->material->texture; if(face->tex_image && face->tex_image->width) face->flags |= G3D_FLAG_FAC_TEXMAP; else { face->tex_ncoords = 0; if(face->tex_coords) g_free(face->tex_coords); } fitem = fitem->next; } g3d_interface_update(); oitem = oitem->next; } } static G3DImage *q3o_get_texture_nth(G3DModel *model, guint32 n) { gchar number[32]; G3DImage *image; if(model->texture_files == NULL) model->texture_files = g_hash_table_new(g_str_hash, g_str_equal); #if DEBUG > 5 g_print("Q3O: texture #%d wanted\n", n); #endif sprintf(number, "%d", n); image = g_hash_table_lookup(model->texture_files, number); if(image) { #if DEBUG > 5 g_print("Q3O: texture #%d from hash table\n", n); #endif return image; } #if DEBUG > 5 g_print("Q3O: texture #%d created\n", n); #endif image = g_new0(G3DImage, 1); image->scaleu = 1.0; image->scalev = 1.0; image->name = g_strdup_printf("would be %d", n + 1); g_hash_table_insert(model->texture_files, g_strdup(number), image); return image; } static int q3o_read_mesh(FILE *f, G3DModel *model, guint32 n_textures) { guint32 i, j, nfaces, mat, nnormals, ntexco, index; guint16 *faceshapes; GSList *fitem; G3DObject *object; G3DFace *face; G3DMaterial *material; object = g_new0(G3DObject, 1); object->name = g_strdup("Q3O mesh"); model->objects = g_slist_append(model->objects, object); material = g3d_new_G3DMaterial(); material->name = g_strdup("fallback material"); object->materials = g_slist_append(object->materials, material); /* vertices */ object->vertex_count = g3d_read_int32_le(f); #if DEBUG > 3 g_print("Q3O: number of vertices: %d\n", object->vertex_count); #endif object->vertices = g_malloc0(object->vertex_count * 3 * sizeof(GLfloat)); for(i = 0; i < object->vertex_count; i ++) { object->vertices[i*3+0] = g3d_read_float_le(f); object->vertices[i*3+1] = g3d_read_float_le(f); object->vertices[i*3+2] = g3d_read_float_le(f); g3d_interface_update(); } /* faces */ nfaces = g3d_read_int32_le(f); #if DEBUG > 3 g_print("Q3O: number of faces: %d\n", nfaces); #endif faceshapes = g_new0(guint16, nfaces); for(i = 0; i < nfaces; i ++) { faceshapes[i] = g3d_read_int16_le(f); g3d_interface_update(); } for(i = 0; i < nfaces; i ++) { face = g_new0(G3DFace, 1); face->num_vertices = faceshapes[i]; face->index_vertex = g_new0(guint32, face->num_vertices); for(j = 0; j < face->num_vertices; j ++) { face->index_vertex[j] = g3d_read_int32_le(f); if(face->index_vertex[j] >= object->vertex_count) { g_warning("Q3O: index_vertex >= vertex_count"); } g3d_interface_update(); } /* fallback material */ face->material = (G3DMaterial *)g_slist_nth_data(object->materials, 0); g_assert(face->material); object->faces = g_slist_append(object->faces, face); g3d_interface_update(); } /* material indices */ fitem = object->faces; for(i = 0; i < nfaces; i ++) { face = (G3DFace *)fitem->data; g_assert(face != NULL); mat = g3d_read_int32_le(f); face->material = (G3DMaterial*)g_slist_nth_data(model->materials, mat); if(face->material == NULL) { if(mat != -1) g_warning("Q3O: material is NULL (index %d)", mat); face->material = g_slist_nth_data(object->materials, 0); } g3d_interface_update(); fitem = fitem->next; } /* normals: TODO */ nnormals = g3d_read_int32_le(f); #if DEBUG > 3 g_print("Q3O: number of normals: %d\n", nnormals); #endif for(i = 0; i < nnormals; i ++) { g3d_read_float_le(f); g3d_read_float_le(f); g3d_read_float_le(f); g3d_interface_update(); } /* texture stuff */ ntexco = g3d_read_int32_le(f); #if DEBUG > 3 g_print("Q3O: number of texture coordinates: %d\n", ntexco); #endif if(n_textures > 0) { object->tex_nverts = ntexco; object->tex_vertices = g_new0(GLfloat, 2 * ntexco); for(i = 0; i < ntexco; i ++) { object->tex_vertices[i * 2 + 0] = g3d_read_float_le(f); object->tex_vertices[i * 2 + 1] = g3d_read_float_le(f); } fitem = object->faces; for(i = 0; i < nfaces; i ++) { face = (G3DFace *)fitem->data; face->tex_ncoords = faceshapes[i]; face->tex_coords = g_new0(GLfloat, faceshapes[i] * 2); for(j = 0; j < faceshapes[i]; j ++) { index = g3d_read_int32_le(f); face->tex_coords[j * 2 + 0] = object->tex_vertices[face->index_vertex[j] * 2 + 0]; face->tex_coords[j * 2 + 1] = object->tex_vertices[face->index_vertex[j] * 2 + 1]; } g3d_interface_update(); fitem = fitem->next; } } /* centerOfMass */ g3d_read_float_le(f); g3d_read_float_le(f); g3d_read_float_le(f); /* boundingBox */ g3d_read_float_le(f); g3d_read_float_le(f); g3d_read_float_le(f); g3d_read_float_le(f); g3d_read_float_le(f); g3d_read_float_le(f); /* clean up */ g_free(faceshapes); if(object->tex_vertices) { /* should be in faces */ g_free(object->tex_vertices); object->tex_vertices = NULL; } g3d_interface_update(); return EXIT_SUCCESS; } static int q3o_read_material(FILE *f, G3DModel *model, guint32 index, guint32 n_textures) { gchar buffer[2048], *bufp; G3DMaterial *material; gint32 num; material = g_slist_nth_data(model->materials, index); memset(buffer, 0, 2048); bufp = buffer; while((*bufp = g3d_read_int8(f)) != '\0') bufp ++; material->name = g_strdup(buffer); #if DEBUG > 0 g_print("Q3O: material name: '%s'\n", buffer); #endif /* ambientColor */ material->r = g3d_read_float_le(f); material->g = g3d_read_float_le(f); material->b = g3d_read_float_le(f); /* diffuseColor */ material->r = g3d_read_float_le(f); material->g = g3d_read_float_le(f); material->b = g3d_read_float_le(f); /* specularColor */ material->specular[0] = g3d_read_float_le(f); material->specular[1] = g3d_read_float_le(f); material->specular[2] = g3d_read_float_le(f); /* transparency */ material->a = g3d_read_float_le(f); if(material->a == 0.0) material->a = 1.0; if(material->a < 0.1) material->a = 0.1; /* texture */ num = g3d_read_int32_le(f); #if DEBUG > 4 g_print("Q3O: material unknown uint32: %d\n", num); #endif if((num != -1) && (num < n_textures)) material->texture = q3o_get_texture_nth(model, num); return EXIT_SUCCESS; } static int q3o_read_texture(FILE *f, G3DModel *model) { G3DImage *image; gchar buffer[2048], *bufp; #if DEBUG > 2 gchar *ppmname; #endif guint32 width, height, y, x; static guint32 index = 0; memset(buffer, 0, 2048); bufp = buffer; while((*bufp = g3d_read_int8(f)) != '\0') bufp ++; width = g3d_read_int32_le(f); height = g3d_read_int32_le(f); #if DEBUG > 0 g_print("Q3O: texture #%d '%s': %dx%d\n", index, buffer, width, height); #endif image = q3o_get_texture_nth(model, index); index ++; image->name = g_strdup(buffer); image->width = width; image->height = height; image->depth = 32; image->pixeldata = g_new0(guint8, width * height * 4); image->gl_texid = index; for(y = 0; y < height; y ++) for(x = 0; x < width; x ++) { image->pixeldata[(y * width + x) * 4 + 0] = g3d_read_int8(f); image->pixeldata[(y * width + x) * 4 + 1] = g3d_read_int8(f); image->pixeldata[(y * width + x) * 4 + 2] = g3d_read_int8(f); image->pixeldata[(y * width + x) * 4 + 3] = 0xFF; } #if DEBUG > 2 ppmname = g_strdup_printf("/tmp/%s.ppm", image->name); g3d_image_dump_ppm(image, ppmname); g_free(ppmname); #endif return EXIT_SUCCESS; } static int q3o_read_scene(FILE *f, G3DModel *model) { gchar buffer[2048], *bufp; guint32 bgw, bgh; /* position: 3 x float */ fseek(f, 12, SEEK_CUR); /* transformation: matrix */ fseek(f, 64, SEEK_CUR); /* axis: 3 x float */ fseek(f, 12, SEEK_CUR); /* angle: float */ fseek(f, 4, SEEK_CUR); /* eyePosition: 3 x float */ fseek(f, 12, SEEK_CUR); /* eyeRotation: 3 x float */ fseek(f, 12, SEEK_CUR); /* foregroundColor: color */ fseek(f, 12, SEEK_CUR); /* backgroundColor: color */ model->bgcolor[0] = g3d_read_float_le(f); model->bgcolor[1] = g3d_read_float_le(f); model->bgcolor[2] = g3d_read_float_le(f); /* usingEyeFilter: bool */ g3d_read_int8(f); /* eyeFilterColor: color */ fseek(f, 12, SEEK_CUR); /* eyeFilterAmount: float */ g3d_read_float_le(f); /* lightColor: color */ fseek(f, 12, SEEK_CUR); /* backgroundImageWidth: int */ bgw = g3d_read_int32_le(f); /* backgroundImageHeight: int */ bgh = g3d_read_int32_le(f); if(bgw * bgh) { /* backgroundFilename */ memset(buffer, 0, 2048); bufp = buffer; while((*bufp = g3d_read_int8(f)) != '\0') bufp ++; #if DEBUG > 0 g_print("Q3O: scene: background image '%s' (%dx%d)\n", buffer, bgw, bgh); #endif /* backgroundImage: pixel[] */ fseek(f, bgw * bgh * 3, SEEK_CUR); } /* depthCuing: float */ g3d_read_float_le(f); /* depthCueColor: color */ fseek(f, 12, SEEK_CUR); /* gamma: float */ g3d_read_float_le(f); return EXIT_FAILURE; } static int q3o_read_eof(FILE *f) { gchar buffer[8]; fseek(f, -1, SEEK_CUR); if(fread(buffer, 1, 8, f) == 8) { if(strncmp(buffer, "quick3Ds", 8) == 0) return EXIT_SUCCESS; g_warning("Q3O: did not get expected EOF marker"); } else { g_warning("Q3O: premature end of file\n"); } return EXIT_FAILURE; }