//
// File: vrml2_mesh.cc
//
// (C) 2000-2008 Helmut Cantzler
//
// Licensed under the terms of the Lesser General Public License.
//
#include <string.h>
#include "vrml2_mesh.h"
#include "matrix3.h"
int Vrml2Mesh::readNextToken(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 ((*updateProgress)(ftell(f)))
{
s[0]=0;
res=EOF;
}
return res;
}
int Vrml2Mesh::readVertex(FILE *f, float *x, float *y, float *z)
{
if (readNextToken(f, buffer) == EOF || sscanf(buffer, "%f", x) != 1)
return FALSE;
if (readNextToken(f, buffer) == EOF || sscanf(buffer, "%f", y) != 1)
return FALSE;
if (readNextToken(f, buffer) == EOF || sscanf(buffer, "%f%*c", z) != 1)
return FALSE;
return TRUE;
}
int Vrml2Mesh::readTriangle(FILE *f, int *p1, int *p2, int *p3)
{
if (readNextToken(f, buffer) == EOF || sscanf(buffer, "%d%*c", p1) != 1)
return FALSE;
if (readNextToken(f, buffer) == EOF || sscanf(buffer, "%d%*c", p2) != 1)
return FALSE;
if (readNextToken(f, buffer) == EOF || sscanf(buffer, "%d%*c", p3) != 1)
return FALSE;
return TRUE;
}
int Vrml2Mesh::readTextCoord(FILE *f, float *s, float *t)
{
if (readNextToken(f, buffer) == EOF || sscanf(buffer, "%f", s) != 1)
return FALSE;
if (readNextToken(f, buffer) == EOF || sscanf(buffer, "%f%*c", t) != 1)
return FALSE;
return TRUE;
}
int Vrml2Mesh::read(FILE *f, int (*update)(int pos),
void (*setTotal)(int size))
{
list<Vertex*> vertices;
char s1[101], s2[21];
updateProgress=update;
fseek(f, 0, SEEK_END);
(*setTotal)(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, shapeNr;
error = shapeNr = 0;
do
{
// look for a starting point
do
{
res = readNextToken(f, s1);
}
while (res != EOF && strcasecmp(s1,"Shape") != 0 &&
strcasecmp(s1,"Transform") != 0);
if (strcasecmp(s1,"Shape") == 0)
error = newShape(f, &vertices, ++shapeNr);
if (strcasecmp(s1,"Transform") == 0)
error = newTransform(f, &vertices, &shapeNr, 1);
}
while (res != EOF);
return error;
}
int Vrml2Mesh::newShape(FILE *f, list<Vertex*> *vertices, int shapeNr)
{
list<Triangle*> *shapeTriangles;
list<Vertex*> *shapeVertices;
vector<Vertex*> vertexIndex;
vector< pair<float, float>* > shapeTexture;
list<int> shapeTextureIndex;
int res, brakesCount;
char textureName[100], s1[101];
shapeTriangles = new list<Triangle*>;
shapeVertices = new list<Vertex*>;
textureName[0]=0;
brakesCount=0;
do
{
res = readNextToken(f, s1);
if (strcmp(s1,"}") == 0)
brakesCount--;
if (strcmp(s1,"{") == 0)
brakesCount++;
// 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(textureName, s1+1);
else
strcpy(textureName, s1);
if (textureName[strlen(textureName)-1] == '\"')
textureName[strlen(textureName)-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 (readVertex(f, &x, &y, &z))
{
v = new Vertex(x,y,z);
addVertex(v);
// save in a vector for the triangles, shape and transform
vertexIndex.push_back(v);
}
// Reads till end of coord
while (fgetc(f) != '}') ;
}
}
// look for triangles
if (strcasecmp(s1,"coordIndex") == 0 && vertexIndex.size() > 0)
{
Triangle *tri;
int p1, p2, p3, end;
// Reads till begin of triangles
while (fgetc(f) != '[') ;
while (readTriangle(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(vertexIndex[p1],
vertexIndex[p2],
vertexIndex[p3]);
addTriangle(tri);
shapeTriangles->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 && vertexIndex.size() > 0)
{
float s, t;
// Reads till begin of texture coordinates
while (fgetc(f) != '[') ;
while (readTextCoord(f, &s, &t))
shapeTexture.push_back(new pair<float,float>(s,t));
// Reads till end of coord
while (fgetc(f) != '}') ;
}
// look for texture coordinate index
if (strcasecmp(s1,"texCoordIndex") == 0 && vertexIndex.size() > 0)
{
int p1, p2, p3, end;
fscanf(f,"%100s", s1);
if (strcasecmp(s1,"[") == 0)
while (readTriangle(f, &p1, &p2, &p3))
{
fscanf(f,"%d%*c",&end);
if (end == -1)
{
// end (-1) is reached => number of points is 3 => ok
shapeTextureIndex.push_back(p1);
shapeTextureIndex.push_back(p2);
shapeTextureIndex.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 && brakesCount != 0);
// copy the vertices of this shape over
// once for the shape and once for the transform
shapeVertices->insert(shapeVertices->end(),
vertexIndex.begin(), vertexIndex.end());
vertices->insert(vertices->end(), vertexIndex.begin(), vertexIndex.end());
vector< pair<float, float>* >::iterator itex;
if (shapeTriangles->size() > 0 && shapeTexture.size() > 0)
// assignment of texture coordiantes with texture index
if (shapeTextureIndex.size() != 0)
{
list<Triangle*>::iterator it;
list<int>::iterator itexi;
itexi=shapeTextureIndex.begin();
for (it=shapeTriangles->begin(); it != shapeTriangles->end(); it++)
for (int i=0; i < 3; i++, itexi++)
(*it)->setTextCoordinates(i, shapeTexture[*itexi]);
}
// assignment of texture coordiantes without texture index
else
{
vector<Vertex*>::iterator iv;
itex=shapeTexture.begin();
for(iv=vertexIndex.begin(); iv != vertexIndex.end(); iv++, itex++)
(*iv)->setTextCoordinates(*itex);
}
for(itex=shapeTexture.begin(); itex != shapeTexture.end(); itex++)
delete (*itex);
// printf(" Shape: %d vertices and %d trinagles\n", shapeVertices.size(),
// shapeTriangles->size());
shapes->push_back(new Shape(shapeTriangles, shapeVertices, textureName));
return 0;
}
int Vrml2Mesh::newTransform(FILE *f, list<Vertex*> *vertices, int *shapes, int iter)
{
list<Vertex*> transformVertices;
list<Vertex*>::iterator iv;
float trans[3], rota[4], scale[3];
int res, brakesCount;
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 = brakesCount = 0;
do
{
res = readNextToken(f, s1);
if (strcmp(s1,"}") == 0)
brakesCount--;
if (strcmp(s1,"{") == 0)
brakesCount++;
// 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)
if (newShape(f, &transformVertices, ++(*shapes)) != 0)
return 1;
// looking for transforms
if (strcasecmp(s1,"Transform") == 0)
if (newTransform(f, &transformVertices, shapes, iter+1) != 0)
return 1;
}
while (res != EOF && brakesCount != 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<float> r, a, b(B), c(C);
a.setIdentity();
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=transformVertices.begin(); iv != transformVertices.end(); iv++)
{
(*iv)->rotate(&r);
(*iv)->scale(scale[0], scale[1], scale[2]);
(*iv)->move(trans[0], trans[1], trans[2]);
// copy over
vertices->push_back(*iv);
}
return 0;
}
void Vrml2Mesh::write(FILE *f, const char *comment)
{
int n, i, j, isTextured;
list<Triangle*>::iterator it;
list<Triangle*> *shapeTriangles;
list<Vertex*>::iterator iv;
list<Vertex*> *shapeVertices;
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 < numberOfShapes(); i++)
{
const char *textureName = getShape(i)->getTextureName();
isTextured = textureName != NULL && strcmp(textureName,"") != 0;
shapeVertices = getShape(i)->vertices;
shapeTriangles = getShape(i)->triangles;
fprintf(f,"Shape {\n");
fprintf(f,"\tappearance Appearance {\n");
if (isTextured)
{
fprintf(f, "\t\ttexture ImageTexture {\n");
fprintf(f, "\t\t\turl \"%s\"\n", textureName);
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 (shapeVertices != NULL && shapeVertices->size() > 0)
{
n=0;
for (iv=shapeVertices->begin(); iv != shapeVertices->end(); iv++)
{
calcOriginalCoordinates(*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 (shapeTriangles != NULL && shapeTriangles->size() > 0)
{
for (it=shapeTriangles->begin(); it != shapeTriangles->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 (isTextured && shapeTriangles != NULL && shapeTriangles->size() > 0)
{
map< Vertex*, pair<float, float> > textMap;
map< Vertex*, pair<float, float> >::iterator iTextMap;
pair<float, float> st;
// save texture coordinates per vertex
// vertices only have one pair of texture coordinates
// in one shape
for (it=shapeTriangles->begin(); it != shapeTriangles->end(); it++)
for (j=0; j < 3; j++)
textMap[ (*it)->vertices[j] ] = pair<float, float>
((*it)->getTextS(j), (*it)->getTextT(j));
fprintf(f,"\t\ttexCoord TextureCoordinate {\n");
fprintf(f,"\t\t\tpoint [");
for (iv=shapeVertices->begin(); iv != shapeVertices->end(); iv++)
{
iTextMap=textMap.find(*iv);
if (iTextMap == textMap.end())
fprintf(f,"\n\t\t\t\t0.0 0.0");
else
{
st = (*iTextMap).second;
fprintf(f,"\n\t\t\t\t%f %f", st.first, st.second);
}
}
fprintf(f," ]\n\t\t}\n");
fprintf(f,"\t\ttexCoordIndex [");
for (it=shapeTriangles->begin(); it != shapeTriangles->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=shapeTriangles->begin(); it != shapeTriangles->end(); it++)
for (j=0; j < 3; j++)
fprintf(f,"\t\t\t\t%f %f", (*it)->getTextS(j),
(*it)->getTextT(j));
fprintf(f," ]\n\t\t}\n");
fprintf(f,"\t\ttexCoordIndex [\n");
n=0;
for (it=shapeTriangles->begin(); it != shapeTriangles->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");
}