#ifdef HAVE_CONFIG_H # include #endif #include #include #include #include #include #define X3DMF_CHUNK_CHAR(id, shift) \ ((((id) >> (shift)) & 0xFF) == 0) ? \ ' ' : ((id) >> (shift)) & 0xFF static int x3dmf_read_container(FILE *f, guint32 length, G3DModel *model, G3DObject *object, guint32 level); int plugin_load(const char *filename, G3DModel *model) { guint32 id, len, flags, tocloc; guint16 ver_min, ver_maj; FILE *f; gchar txthead[10]; f = fopen(filename, "rb"); if(f == NULL) { g_warning("failed to open file %s", filename); return EXIT_FAILURE; } g3d_iff_readchunk(f, &id, &len); if((id != G3D_IFF_MKID('3', 'D', 'M', 'F')) || (len != 16)) { fseek(f, 0, SEEK_SET); fread(txthead, sizeof(gchar), 10, f); if(strncmp(txthead, "3DMetafile", 10) == 0) { g_warning("file %s is an ASCII 3D Metafile (unhandled)\n", filename); } else { g_warning("file %s is not a 3D Metafile\n", filename); } return EXIT_FAILURE; } ver_maj = g3d_read_int16_be(f); ver_min = g3d_read_int16_be(f); flags = g3d_read_int32_be(f); tocloc = g3d_read_int32_be(f); fseek(f, 4, SEEK_CUR); /* skip padding */ #if DEBUG > 0 g_print("3DMF: version %d.%d (0x%08x) TOC @ 0x%08x\n", ver_maj, ver_min, flags, tocloc); #endif x3dmf_read_container(f, (guint32) -1, model, NULL, 0); fclose(f); return EXIT_SUCCESS; } char *plugin_description(void) { return g_strdup("import plugin for 3D Metafiles\n"); } char **plugin_extensions(void) { return g_strsplit("b3d:3mf:3dmf", ":", 0); } /* * 3DMF specific */ static int x3dmf_read_mesh(FILE *f, G3DObject *object) { guint32 i, j, nconts, nfaces, nbytes = 0; G3DFace *face; object->vertex_count = g3d_read_int32_be(f); object->vertices = g_malloc(object->vertex_count * 3 * sizeof(GLfloat)); nbytes += 4; for(i = 0; i < object->vertex_count; i ++) { object->vertices[i*3+0] = g3d_read_float_be(f); object->vertices[i*3+1] = g3d_read_float_be(f); object->vertices[i*3+2] = g3d_read_float_be(f); nbytes += 12; g3d_interface_update(); } nfaces = g3d_read_int32_be(f); nconts = g3d_read_int32_be(f); nbytes += 8; for(i = 0; i < nfaces; i ++) { face = g_malloc0(sizeof(G3DFace)); face->num_vertices = g3d_read_int32_be(f); nbytes += 4; face->index_vertex = g_malloc(face->num_vertices * sizeof(guint32)); for(j = 0; j < face->num_vertices; j ++) { face->index_vertex[j] = g3d_read_int32_be(f); nbytes += 4; } face->material = g_slist_nth_data(object->materials, 0); object->faces = g_slist_prepend(object->faces, face); g3d_interface_update(); } return nbytes; } static int x3dmf_read_tmsh(FILE *f, G3DObject *object) { G3DFace *face; guint32 nread = 0, nfaces, nverts, i; nfaces = g3d_read_int32_be(f); nread += 4; g3d_read_int32_be(f); g3d_read_int32_be(f); g3d_read_int32_be(f); nread += 12; nverts = g3d_read_int32_be(f); nread += 4; g3d_read_int32_be(f); nread += 4; for(i = 0; i < nfaces; i ++) { face = g_new0(G3DFace, 1); face->num_vertices = 3; face->index_vertex = g_new0(guint32, 3); face->index_vertex[0] = g3d_read_int16_be(f); face->index_vertex[1] = g3d_read_int16_be(f); face->index_vertex[2] = g3d_read_int16_be(f); nread += 6; face->material = g_slist_nth_data(object->materials, 0); object->faces = g_slist_prepend(object->faces, face); } object->vertex_count = nverts; object->vertices = g_new0(GLfloat, 3 * nverts); for(i = 0; i < nverts; i ++) { object->vertices[i * 3 + 0] = g3d_read_float_be(f); object->vertices[i * 3 + 1] = g3d_read_float_be(f); object->vertices[i * 3 + 2] = g3d_read_float_be(f); nread += 12; } return nread; } static int x3dmf_read_container(FILE *f, guint32 length, G3DModel *model, G3DObject *object, guint32 level) { G3DMaterial *material = NULL; guint32 len, id, chk, i; GLfloat matrix[16]; while(length > 0) { if(feof(f)) break; g3d_iff_readchunk(f, &id, &len); length -= 8; if(id == 0) return EXIT_FAILURE; #if DEBUG > 1 g_print("%.*s[%c%c%c%c]: %d bytes\n", level * 2, " ", X3DMF_CHUNK_CHAR(id, 24), X3DMF_CHUNK_CHAR(id, 16), X3DMF_CHUNK_CHAR(id, 8), X3DMF_CHUNK_CHAR(id, 0), len); #endif length -= len; switch(id) { case G3D_IFF_MKID('a', 'm', 'b', 'n'): /* ambient light */ break; case G3D_IFF_MKID('a', 't', 't', 'r'): /* attribute set */ break; case G3D_IFF_MKID('b', 'g', 'n', 'g'): /* begin group */ fseek(f, len, SEEK_CUR); break; case G3D_IFF_MKID('c', 'n', 't', 'r'): /* container */ x3dmf_read_container(f, len, model, object, level + 1); level ++; break; case G3D_IFF_MKID('c', 's', 'p', 'c'): /* specular control */ g3d_read_float_be(f); break; case G3D_IFF_MKID('c', 't', 'w', 'n'): /* interactive renderer */ break; case G3D_IFF_MKID('e', 'n', 'd', 'g'): /* end group */ break; case G3D_IFF_MKID('k', 'd', 'i', 'f'): /* diffuse color */ if(object) { #if DEBUG > 2 g_print("3DMF: kdif: got object\n"); #endif material = g_slist_nth_data(object->materials, 0); material->r = g3d_read_float_be(f); material->g = g3d_read_float_be(f); material->b = g3d_read_float_be(f); } else { fseek(f, len, SEEK_CUR); } break; case G3D_IFF_MKID('k', 's', 'p', 'c'): /* specular color */ if(object) { #if DEBUG > 2 g_print("3DMF: kspc: got object\n"); #endif material = g_slist_nth_data(object->materials, 0); material->specular[0] = g3d_read_float_be(f); material->specular[1] = g3d_read_float_be(f); material->specular[2] = g3d_read_float_be(f); } else { fseek(f, len, SEEK_CUR); } break; case G3D_IFF_MKID('k', 'x', 'p', 'r'): /* transparency color */ if(object) { /* use average as alpha */ material = g_slist_nth_data(object->materials, 0); material->a = 1.0 - (g3d_read_float_be(f) + g3d_read_float_be(f) + g3d_read_float_be(f)) / 3.0; if(material->a < 0.1) material->a = 0.1; } else { fseek(f, len, SEEK_CUR); } break; case G3D_IFF_MKID('l', 'g', 'h', 't'): /* light data */ /* isOn */ g3d_read_int32_be(f); /* intensity */ g3d_read_int32_be(f); /* color */ g3d_read_float_be(f); g3d_read_float_be(f); g3d_read_float_be(f); break; case G3D_IFF_MKID('m', 'e', 's', 'h'): /* mesh */ object = g_new0(G3DObject, 1); material = g3d_new_G3DMaterial(); object->name = g_strdup_printf("mesh @ 0x%08lx", ftell(f) - 8); model->objects = g_slist_append(model->objects, object); object->materials = g_slist_append(object->materials, material); chk = x3dmf_read_mesh(f, object); if(chk != len) { g_warning("3DMF: mesh: wrong length (%u != %u)\n", chk, len); return EXIT_FAILURE; } break; case G3D_IFF_MKID('m', 't', 'r', 'x'): /* matrix */ for(i = 0; i < 16; i ++) matrix[i] = g3d_read_float_be(f); if(object) { #if DEBUG > 2 g_print("3DMF: mtrx: object is set\n"); #endif for(i = 0; i < object->vertex_count; i ++) { g3d_vector_transform( &(object->vertices[i * 3 + 0]), &(object->vertices[i * 3 + 1]), &(object->vertices[i * 3 + 2]), matrix); g3d_interface_update(); } } #if DEBUG > 3 for(i = 0; i < 4; i ++) g_print("3DMF: mtrx: %+1.2f %+1.2f %+1.2f %+1.2f\n", matrix[i * 4 + 0], matrix[i * 4 + 1], matrix[i * 4 + 2], matrix[i * 4 + 3]); #endif break; case G3D_IFF_MKID('n', 'r', 'm', 'l'): /* normal */ fseek(f, 12, SEEK_CUR); break; case G3D_IFF_MKID('s', 'e', 't', ' '): /* ??: skip this cntr chunk */ fseek(f, length, SEEK_CUR); length = 0; break; case G3D_IFF_MKID('t', 'm', 's', 'h'): /* triangle mesh */ object = g_new0(G3DObject, 1); material = g3d_new_G3DMaterial(); object->name = g_strdup_printf("mesh @ 0x%08lx", ftell(f) - 8); model->objects = g_slist_append(model->objects, object); object->materials = g_slist_append(object->materials, material); chk = x3dmf_read_tmsh(f, object); if(chk != len) { #if DEBUG > 0 g_print("3DMF: tmsh: offset %d bytes\n", len - chk); #endif fseek(f, len - chk, SEEK_CUR); } break; case G3D_IFF_MKID('v', 'a', 'n', 'a'): /* view angle aspect camera */ /* fieldOfView */ g3d_read_float_be(f); /* aspectRatioXtoY */ g3d_read_float_be(f); break; case G3D_IFF_MKID('v', 'a', 's', 'l'): /* vertex attribute set list */ fseek(f, len, SEEK_CUR); break; default: #if DEBUG > 0 g_print("3DMF: Container: unknown chunk '%c%c%c%c' @ 0x%08lx " "(%d bytes)\n", X3DMF_CHUNK_CHAR(id, 24), X3DMF_CHUNK_CHAR(id, 16), X3DMF_CHUNK_CHAR(id, 8), X3DMF_CHUNK_CHAR(id, 0), ftell(f) - 8, len); #endif fseek(f, len, SEEK_CUR); break; } } return EXIT_SUCCESS; }