// // File: vrml2_mesh.cc // // (C) 2000-2006 Helmut Cantzler // // Licensed under the terms of the Lesser General Public License. // #include "vrml2_mesh.h" #include "matrix3.h" int Vrml2_Mesh::read_next_token(FILE *f, char *s) { int len, res; res=fscanf(f,"%100s", s); // check if it is a comment while (res != EOF && s[0] == '#') { do // Reads till end of the line { res=fgetc(f); } while (res != EOF && res != '\n'); res=fscanf(f,"%100s",s); } // separate "{" and "}" (bad file formating) len= (int) strlen(s); if (len > 1) { if (s[len-1] == '{') { s[len-1]=0; ungetc('{', f); } if (s[len-1] == '}') { s[len-1]=0; ungetc('}', f); } } // separate "[" and "]" (bad file formating) if (len > 1) { if (s[len-1] == '[') { s[len-1]=0; ungetc('[', f); } if (s[len-1] == ']') { s[len-1]=0; ungetc(']', f); } } if ((*update_progress)(ftell(f))) { s[0]=0; res=EOF; } return res; } int Vrml2_Mesh::read_vertex(FILE *f, float *x, float *y, float *z) { if (read_next_token(f, buffer) == EOF || sscanf(buffer, "%f", x) != 1) return FALSE; if (read_next_token(f, buffer) == EOF || sscanf(buffer, "%f", y) != 1) return FALSE; if (read_next_token(f, buffer) == EOF || sscanf(buffer, "%f%*c", z) != 1) return FALSE; return TRUE; } int Vrml2_Mesh::read_triangle(FILE *f, int *p1, int *p2, int *p3) { if (read_next_token(f, buffer) == EOF || sscanf(buffer, "%d%*c", p1) != 1) return FALSE; if (read_next_token(f, buffer) == EOF || sscanf(buffer, "%d%*c", p2) != 1) return FALSE; if (read_next_token(f, buffer) == EOF || sscanf(buffer, "%d%*c", p3) != 1) return FALSE; return TRUE; } int Vrml2_Mesh::read_text_coord(FILE *f, float *s, float *t) { if (read_next_token(f, buffer) == EOF || sscanf(buffer, "%f", s) != 1) return FALSE; if (read_next_token(f, buffer) == EOF || sscanf(buffer, "%f%*c", t) != 1) return FALSE; return TRUE; } int Vrml2_Mesh::read(FILE *f, int (*update)(int pos), void (*set_total)(int size)) { list transform_vertices; char s1[101], s2[21]; update_progress=update; fseek(f, 0, SEEK_END); (*set_total)(ftell(f)); fseek(f, 0, SEEK_SET); if (fscanf(f,"%100s %20s", s1, s2) != 2) // Read header { FILE_ERROR(f, "VRML header format error"); return 2; } while (fgetc(f) != '\n') // Reads till end of the line ; // check header if (strcasecmp(s1,"#VRML") != 0 || strcasecmp(s2,"V2.0") != 0) { FILE_ERROR(f, "VRML2 header format error"); return 3; } int error, res, shape_nr; error = shape_nr = 0; do { // look for a starting point do { res = read_next_token(f, s1); } while (res != EOF && strcasecmp(s1,"Shape") != 0 && strcasecmp(s1,"Transform") != 0 && strcasecmp(s1,"Group") != 0); if (res != EOF) error = new_transform(f, &transform_vertices, &shape_nr, s1, 1); } while (res != EOF); return error; } int Vrml2_Mesh::new_shape(FILE *f, list *transform_vertices, int shape_nr) { list *shape_triangles; list *shape_vertices; vector vertex_index; vector< pair* > shape_texture; list shape_texture_index; int res, brakes_count; char texture_name[100], s1[101]; shape_triangles = new list; shape_vertices = new list; texture_name[0]=0; brakes_count=0; do { res = read_next_token(f, s1); if (strcmp(s1,"}") == 0) brakes_count--; if (strcmp(s1,"{") == 0) brakes_count++; // look for texture if (strcasecmp(s1,"ImageTexture") == 0) { // Find the file name while (strcasecmp(s1,"url") != 0) res=fscanf(f,"%100s",s1); res=fscanf(f,"%100s", s1); if (strcasecmp(s1,"[") == 0) res=fscanf(f,"%100s", s1); // remove heading and following \" if (s1[0] == '\"') strcpy(texture_name, s1+1); else strcpy(texture_name, s1); if (texture_name[strlen(texture_name)-1] == '\"') texture_name[strlen(texture_name)-1]=0; // Reads till end of texture while (fgetc(f) != '}') ; } // look for points if (strcasecmp(s1,"coord") == 0) { res=fscanf(f,"%100s",s1); // defining a point set if (strcasecmp(s1,"def") == 0) ; // fix me !!! // using an old points set if (strcasecmp(s1,"use") == 0) ; // fix me !!! // normal points else { Vertex *v; float x, y, z; // Reads till begin of points while (fgetc(f) != '[') ; while (read_vertex(f, &x, &y, &z)) { v = new Vertex(x,y,z); add_vertex(v); // save in a vector for the triangles, shape and transform vertex_index.push_back(v); } // Reads till end of coord while (fgetc(f) != '}') ; } } // look for triangles if (strcasecmp(s1,"coordIndex") == 0 && vertex_index.size() > 0) { Triangle *tri; int p1, p2, p3, end; // Reads till begin of triangles while (fgetc(f) != '[') ; while (read_triangle(f, &p1, &p2, &p3)) { fscanf(f,"%d%*c",&end); if (end == -1) { // end (-1) is reached => number of points is 3 => ok tri = new Triangle(vertex_index[p1], vertex_index[p2], vertex_index[p3]); add_triangle(tri); shape_triangles->push_back(tri); } else // no end (-1) => seek to end (-1) while (end != -1 && res == 1) res=fscanf(f,"%d%*c",&end); } } // look for texture coordinates if (strcasecmp(s1,"texCoord") == 0 && vertex_index.size() > 0) { float s, t; // Reads till begin of texture coordinates while (fgetc(f) != '[') ; while (read_text_coord(f, &s, &t)) shape_texture.push_back(new pair(s,t)); // Reads till end of coord while (fgetc(f) != '}') ; } // look for texture coordinate index if (strcasecmp(s1,"texCoordIndex") == 0 && vertex_index.size() > 0) { int p1, p2, p3, end; fscanf(f,"%100s", s1); if (strcasecmp(s1,"[") == 0) while (read_triangle(f, &p1, &p2, &p3)) { fscanf(f,"%d%*c",&end); if (end == -1) { // end (-1) is reached => number of points is 3 => ok shape_texture_index.push_back(p1); shape_texture_index.push_back(p2); shape_texture_index.push_back(p3); } else // no end (-1) => seek to end (-1) while (end != -1 && res == 1) res=fscanf(f,"%d%*c",&end); } } } while (res != EOF && brakes_count != 0); // copy the vertices of this shape over // once for the shape and once for the transform shape_vertices->insert(shape_vertices->end(), vertex_index.begin(), vertex_index.end()); transform_vertices->insert(transform_vertices->end(), vertex_index.begin(), vertex_index.end()); vector< pair* >::iterator itex; if (shape_triangles->size() > 0 && shape_texture.size() > 0) // assignment of texture coordiantes with texture index if (shape_texture_index.size() != 0) { list::iterator it; list::iterator itexi; itexi=shape_texture_index.begin(); for (it=shape_triangles->begin(); it != shape_triangles->end(); it++) for (int i=0; i < 3; i++, itexi++) (*it)->set_text_coordinates(i, shape_texture[*itexi]); } // assignment of texture coordiantes without texture index else { vector::iterator iv; itex=shape_texture.begin(); for(iv=vertex_index.begin(); iv != vertex_index.end(); iv++, itex++) (*iv)->set_text_coordinates(*itex); } for(itex=shape_texture.begin(); itex != shape_texture.end(); itex++) delete (*itex); // printf(" Shape: %d vertices and %d trinagles\n", shape_vertices.size(), // shape_triangles->size()); shapes->push_back(new Shape(shape_triangles, shape_vertices, texture_name)); return 0; } int Vrml2_Mesh::new_transform(FILE *f, list *transform_vertices, int *shapes, char *start, int iter) { list::iterator iv; float trans[3], rota[4], scale[3]; int res, brakes_count; char s1[100]; // init transform variables trans[0]=trans[1]=trans[2]=0.0; rota[0]=rota[1]=rota[2]=rota[3]=0.0; scale[0]=scale[1]=scale[2]=1.0; // for (int i=0; i < iter; i++) // putchar(' '); // printf("Transform...\n"); res = brakes_count = 0; do { if (start[0] == 0) res = read_next_token(f, s1); else { strcpy(s1, start); start[0]=0; } if (strcmp(s1,"}") == 0) brakes_count--; if (strcmp(s1,"{") == 0) brakes_count++; // looking for transformation stuff if (strcasecmp(s1,"translation") == 0) res=fscanf(f,"%f %f %f",&trans[0],&trans[1],&trans[2]); if (strcasecmp(s1,"rotation") == 0) res=fscanf(f,"%f %f %f %f",&rota[0],&rota[1],&rota[2],&rota[3]); if (strcasecmp(s1,"scale") == 0) res=fscanf(f,"%f %f %f",&scale[0],&scale[1],&scale[2]); // looking for shapes if (strcasecmp(s1,"Shape") == 0) { //for (int i=0; i < iter+1; i++) // putchar(' '); //printf("Shape...\n"); if (new_shape(f, transform_vertices, ++(*shapes)) != 0) return 1; } // looking for a new transform if (strcasecmp(s1,"Transform") == 0) { list v; if (new_transform(f, &v, shapes, "", iter+1) != 0) return 1; // copy over transform_vertices->insert(transform_vertices->end(), v.begin(), v.end()); } } while (res != EOF && brakes_count != 0); // built the rotation matrix r float B[3][3] = {{0, -rota[2], rota[1]}, {rota[2], 0, -rota[0]}, {-rota[1], rota[0], 0}}; float C[3][3] = {{rota[0]*rota[0], rota[0]*rota[1], rota[0]*rota[2]}, {rota[1]*rota[0], rota[1]*rota[1], rota[1]*rota[2]}, {rota[2]*rota[0], rota[2]*rota[1], rota[2]*rota[2]}}; Matrix3 r, a, b(B), c(C); a.set_identity(); a.mul(cos(rota[3])); b.mul(sin(rota[3])); c.mul(1-cos(rota[3])); r.add(&a); r.add(&b); r.add(&c); // do rotation, scaling and translation for (iv=transform_vertices->begin(); iv != transform_vertices->end(); iv++) { (*iv)->rotate(&r); (*iv)->scale(scale[0], scale[1], scale[2]); (*iv)->move(trans[0], trans[1], trans[2]); } return 0; } void Vrml2_Mesh::write(FILE *f, const char *comment) { int n, i, j, is_textured; list::iterator it; list *shape_triangles; list::iterator iv; list *shape_vertices; Vertex v; fprintf(f,"#VRML V2.0 utf8\n"); fprintf(f,"# %s\n", comment); fprintf(f, "Group {\n"); fprintf(f, "\tchildren [\n"); for (i=0; i < number_of_shapes(); i++) { const char *texture_name = get_shape(i)->get_texture_name(); is_textured = texture_name != NULL && strcmp(texture_name,"") != 0; shape_vertices = get_shape(i)->vertices; shape_triangles = get_shape(i)->triangles; fprintf(f,"Shape {\n"); fprintf(f,"\tappearance Appearance {\n"); if (is_textured) { fprintf(f, "\t\ttexture ImageTexture {\n"); fprintf(f, "\t\t\turl \"%s\"\n", texture_name); fprintf(f, "\t\t}\n"); } fprintf(f,"\t}\n"); fprintf(f,"\tgeometry IndexedFaceSet {\n"); fprintf(f,"\t\tcoord Coordinate {\n"); fprintf(f,"\t\t\tpoint ["); if (shape_vertices != NULL && shape_vertices->size() > 0) { n=0; for (iv=shape_vertices->begin(); iv != shape_vertices->end(); iv++) { calc_original_coordinates(*iv, &v); fprintf(f,"\n\t\t\t\t%f %f %f", v.x(), v.y(), v.z()); (*iv)->number=n; n++; } } else fprintf(f,"\n\t\t\t"); fprintf(f," ]\n\t\t}\n"); fprintf(f,"\t\tcoordIndex ["); if (shape_triangles != NULL && shape_triangles->size() > 0) { for (it=shape_triangles->begin(); it != shape_triangles->end(); it++) fprintf(f,"\n\t\t\t\t%d, %d, %d, -1", (*it)->vertices[0]->number, (*it)->vertices[1]->number, (*it)->vertices[2]->number); } else fprintf(f,"\n\t\t"); fprintf(f," ]\n"); if (is_textured && shape_triangles != NULL && shape_triangles->size() > 0) { map< Vertex*, pair > text_map; map< Vertex*, pair >::iterator itm; pair s_t; // save texture coordinates per vertex // vertices only have one pair of texture coordinates // in one shape for (it=shape_triangles->begin(); it != shape_triangles->end(); it++) for (j=0; j < 3; j++) text_map[ (*it)->vertices[j] ] = pair ((*it)->get_text_s(j), (*it)->get_text_t(j)); fprintf(f,"\t\ttexCoord TextureCoordinate {\n"); fprintf(f,"\t\t\tpoint ["); for (iv=shape_vertices->begin(); iv != shape_vertices->end(); iv++) { itm=text_map.find(*iv); if (itm == text_map.end()) fprintf(f,"\n\t\t\t\t0.0 0.0"); else { s_t = (*itm).second; fprintf(f,"\n\t\t\t\t%f %f", s_t.first, s_t.second); } } fprintf(f," ]\n\t\t}\n"); fprintf(f,"\t\ttexCoordIndex ["); for (it=shape_triangles->begin(); it != shape_triangles->end(); it++) fprintf(f,"\n\t\t\t\t%d, %d, %d, -1", (*it)->vertices[0]->number, (*it)->vertices[1]->number, (*it)->vertices[2]->number); fprintf(f," ]\n"); // alternatively, save texture coordinates per triangle // produces much bigger vrml files /* fprintf(f,"\t\ttexCoord TextureCoordinate {\n"); fprintf(f,"\t\t\tpoint [\n"); for (it=shape_triangles->begin(); it != shape_triangles->end(); it++) for (j=0; j < 3; j++) fprintf(f,"\t\t\t\t%f %f", (*it)->get_text_s(j), (*it)->get_text_t(j)); fprintf(f," ]\n\t\t}\n"); fprintf(f,"\t\ttexCoordIndex [\n"); n=0; for (it=shape_triangles->begin(); it != shape_triangles->end(); it++) for (j=0; j < 3; j++, n+=3) fprintf(f,"\t\t\t\t%d, %d, %d, -1", n, n+1, n+2); fprintf(f," ]\n"); */ } fprintf(f,"\t\t}\n}\n"); } fprintf(f, "\t]\n}\n"); }