/* readvrml.C: reads VRML files */ #ifndef NO_VRML #include #include #include "error.h" #include "readvrml.H" #include "xrml.H" #include "renderer.H" #include "Stack.H" #include "nodeCatalog.H" #include "Material.H" #include "TextureTransform.H" #include "Geometry.H" #include "PointSet.H" #include "Viewpoint.H" #include "PhBAppearance.H" #include "Texture.H" extern "C" { #include "vector.h" #include "vertex.h" #include "surface.h" #include "compound.h" #include "geom.h" #include "scene.h" #include "phong.h" #include "splitbsdf.h" #include "patch.h" #include "pools.h" #include "error.h" #include "camera.h" #include "options.h" #include "defaults.h" #include "cie.h" #include "fileopts.h" } #include "PhBRML.H" #include "PhBRMLBackground.H" static xrml::stack geomStack; static GEOMLIST *currentGeomList; static xrml::stack matStack; static MATERIAL *currentMaterial; static TEXTURE *currentTexture; static VECTORLIST *currentCoordList, *currentNormalList, *currentTexCoordList; static VERTEXLIST *currentVertexList; static PATCHLIST *currentPatchList; static xrml::array vert_hash; static xrml::arraycoords; static xrml::arraynormals; // normals explicitly specified in input static xrml::arraybacknormals; // reverse of normals explicitly specified in input static xrml::arraytexCoords;// tex.co's explictly specified in input static VECTOR auto_normal, back_normal; // automatically generated normal and reverse static VECTOR *currentNormal, *currentBackNormal; static VECTOR auto_texCoord; // automaticaly generated tex.co. static VECTOR *currentTexCoord; static RGB currentColor; static xrml::array vert(10), backvert(10); static int nrfaces, skipface; static VERTEX *InstallVertex(int id, VECTOR *coord, VECTOR *normal, VECTOR *texCoord, const RGB& col) { VERTEX *v = (VERTEX *)NULL; if (normal && id > 0) v = VertexListFind(vert_hash[id], coord, normal, texCoord); if (!v) { if (normal == &auto_normal) { // automatically generated normal normal = VectorCreate(auto_normal.x, auto_normal.y, auto_normal.z); currentNormalList = VectorListAdd(currentNormalList, normal); } if (texCoord == &auto_texCoord) { // automatically generated tex.co texCoord = VectorCreate(auto_texCoord.x, auto_texCoord.y, auto_texCoord.z); currentTexCoordList = VectorListAdd(currentTexCoordList, texCoord); } v = VertexCreate(coord, normal, texCoord, PatchListCreate()); v->color = col; if (id > 0) vert_hash[id] = VertexListAdd(vert_hash[id], v); currentVertexList = VertexListAdd(currentVertexList, v); } else { // Add color to vertex color. Vertex colors are normalised when connecting the // patches to a surface (SurfaceConnectFace() in surface.c) v->color.r += col.r; v->color.g += col.g; v->color.b += col.b; } return v; } static void SetDefaultVRMLCamera(void) { VECTOR eyep(0., 0., 10.), lookp(0., 0., 0.), updir(0., 1., 0.); CameraSet(&Camera, &eyep, &lookp, &updir, Camera.fov, Camera.hres, Camera.vres, &Camera.background); } class vrml_importer: public xrml::renderer { ////////////////////////////////////////////////////// // routines for importing VRML'97 geometry and other // standard stuff. ////////////////////////////////////////////////////// xrml::Mat3 xfn; // current transform matrix for normals. xrml::Mat4 txf; // current texture transform int viewpoint_count; bool twosided; // creates a pair of twin patches for every input polygon bool ccw; // counter clock wise vertex order void begin_frame(xrml::world*) { currentTexture = NULL; begin_Material(dynamic_cast(xrml::builtin_nodes->lookup("Material"))); SetDefaultVRMLCamera(); viewpoint_count = 0; } void viewpoint(xrml::Viewpoint *vp) { VECTOR eyep(0., 0., 0.), lookp(0., 0., -10.), updir(0., 1., 0.), view_rotaxis; TRANSFORM view_rot; float fov = vp->fieldOfView; VECTORSET(view_rotaxis, vp->orientation.x, vp->orientation.y, vp->orientation.z); view_rot = Rotate(vp->orientation.radians, view_rotaxis); TRANSFORM_POINT_3D(view_rot, lookp, lookp); VECTORSET(eyep, vp->position.x, vp->position.y, vp->position.z); VECTORADD(lookp, eyep, lookp); /* TRANSFORM_VECTOR_3D(view_rot, updir, updir); */ if (viewpoint_count > 0) CameraPush(&Camera); CameraSet(&Camera, &eyep, &lookp, &updir, fov, Camera.hres, Camera.vres, &Camera.background); viewpoint_count++; } // grouping and transforms void begin_Grouping(xrml::Grouping *) { geomStack.push(currentGeomList); currentGeomList = GeomListCreate(); } void end_Grouping(xrml::Grouping *) { GEOM *newgeom = (GEOM *)NULL; if (currentGeomList) newgeom = GeomCreate(CompoundCreate(currentGeomList), CompoundMethods()); currentGeomList = geomStack.pop(); if (newgeom) currentGeomList = GeomListAdd(currentGeomList, newgeom); } // texture transform void begin_TextureTransform(xrml::TextureTransform *t) { txf = t->xf; } void end_TextureTransform(xrml::TextureTransform *) { txf = xrml::Mat4(); // identity transform } // materials void begin_Material(xrml::Material *matnode) { char *name = strdup(matnode->name()); COLOR Rd, Rs, Td, Ts; double Ns; matStack.push(currentMaterial); #ifdef RGBCOLORS COLORSET(Rd, matnode->diffuseColor.r, matnode->diffuseColor.g, matnode->diffuseColor.b); COLORSET(Rs, matnode->specularColor.r, matnode->specularColor.g, matnode->specularColor.b); #else rgb_xyz((float *)&matnode->diffuseColor, Rd.spec); rgb_xyz((float *)&matnode->specularColor, Rs.spec); #endif Ns = matnode->shininess * 128.; BRDF *brdf = BrdfCreate(PhongBrdfCreate(&Rd, &Rs, Ns), &PhongBrdfMethods); BTDF *btdf = (BTDF*)NULL; if (matnode->transparency > EPSILON) { COLORSETMONOCHROME(Ts, matnode->transparency); COLORCLEAR(Td); btdf = BtdfCreate(PhongBtdfCreate(&Td, &Ts, 1000., 1., 0.), &PhongBtdfMethods); } SPLIT_BSDF *splitbsdf = SplitBSDFCreate(brdf, btdf, disable_textures ? (TEXTURE*)NULL : currentTexture); BSDF *bsdf = BsdfCreate(splitbsdf, &SplitBsdfMethods); EDF *edf = (EDF*)NULL; currentMaterial = MaterialCreate(name, edf, bsdf, 1); MaterialLib = MaterialListAdd(MaterialLib, currentMaterial); } void end_Material(xrml::Material *) { currentMaterial = matStack.pop(); } // TODO: standard VRML textures void begin_Texture(xrml::Texture* t) { if (!t->data.p) { if (!t->Map) t->load(); t->data.p = (void*)CreateTexture(t->Width, t->Height, t->Channels, t->Map); } currentTexture = (TEXTURE*)(t->data.p); } void end_Texture(xrml::Texture* t) { currentTexture = (TEXTURE*)NULL; } // called by the default geometry handler renderer::geometry() void begin_faces(xrml::Geometry *geomnode) { twosided = !geomnode->solid && !force_onesided_surfaces; ccw = geomnode->ccw; currentPatchList = PatchListCreate(); currentCoordList = VectorListCreate(); currentNormalList = VectorListCreate(); currentTexCoordList = VectorListCreate(); currentVertexList = VertexListCreate(); vert_hash.size = 0; vert_hash.grow(geomnode->co->size); coords.size = 0; coords.grow(geomnode->co->size); for (int i=0; ico)[i]) * xf; coords[i] = VectorCreate(v.x, v.y, v.z); currentCoordList = VectorListAdd(currentCoordList, coords[i]); } // inverse of the upper 3x3 part of xf (equals translational part of xf // times inverse of xf = upper 3x3 part of inverse xf). xfn = xrml::Mat3(inverse_xf); normals.size = 0; backnormals.size = 0; if (geomnode->norm) { normals.grow(geomnode->norm->size); if (twosided) backnormals.grow(geomnode->norm->size); for (int i=0; inorm->size; i++) { // transform normals as column vectors (= use transpose of transform) xrml::Vec3 n = xfn * xrml::Vec3((*geomnode->norm)[i]); n.normalize(); normals[i] = VectorCreate(n.x, n.y, n.z); currentNormalList = VectorListAdd(currentNormalList, normals[i]); if (twosided) { backnormals[i] = VectorCreate(-n.x, -n.y, -n.z); currentNormalList = VectorListAdd(currentNormalList, backnormals[i]); } } } // texture coordinates texCoords.size = 0; if (geomnode->texco) { texCoords.grow(geomnode->texco->size); for (int i=0; itexco->size; i++) { xrml::SFVec2f t = (*geomnode->texco)[i]; xrml::Vec3 T = xrml::Vec3(t.s, t.t, 0.) * txf; texCoords[i] = VectorCreate(T.x, T.y, T.z); currentTexCoordList = VectorListAdd(currentTexCoordList, texCoords[i]); } } } void begin_face(int, int) { vert.size = backvert.size = 0; currentNormal = (VECTOR *)NULL; currentBackNormal = (VECTOR *)NULL; currentTexCoord = (VECTOR *)NULL; // POINT p(0.,0.,0.); // COLOR rho = BsdfDiffuseReflectance(currentMaterial->bsdf, &p); //ColorToRGB(rho, ¤tColor); currentColor = White; // TODO: fix this skipface = 0; } void face_normal(int id, const xrml::SFVec3f& norm) { if (id >= 0) { currentNormal = normals[id]; if (twosided) currentBackNormal = backnormals[id]; } else { xrml::Vec3 n = xfn * xrml::Vec3(norm); n.normalize(); VECTORSET(auto_normal, n.x, n.y, n.z); currentNormal = &auto_normal; VECTORSET(back_normal, -n.x, -n.y, -n.z); currentBackNormal = &back_normal; if (n == xrml::Vec3(0,0,0)) skipface = 1; // degenerate face: skip } } void face_color(int, const xrml::SFColor& col) { RGBSET(currentColor, col.r, col.g, col.b); } void vertex_normal(int id, const xrml::SFVec3f& norm) { if (id >= 0) { currentNormal = normals[id]; if (twosided) currentBackNormal = backnormals[id]; } else { xrml::Vec3 n = xfn * xrml::Vec3(norm); n.normalize(); VECTORSET(auto_normal, n.x, n.y, n.z); currentNormal = &auto_normal; VECTORSET(back_normal, -n.x, -n.y, -n.z); currentBackNormal = &back_normal; if (n == xrml::Vec3(0,0,0)) skipface = 1; // degenerate face: skip } } void vertex_color(int, const xrml::SFColor& col) { RGBSET(currentColor, col.r, col.g, col.b); } void vertex_texCoord(int id, const xrml::SFVec2f& texco) { if (id >= 0) currentTexCoord = texCoords[id]; else { xrml::SFVec2f t = texco; xrml::Vec3 T = xrml::Vec3(t.s, t.t, 0.) * txf; VECTORSET(auto_texCoord, T.x, T.y, T.z); currentTexCoord = &auto_texCoord; } } void vertex_coord(int id, const xrml::SFVec3f& /*vertex*/) { if (!skipface) { vert.append(InstallVertex(id, coords[id], currentNormal, currentTexCoord, currentColor)); if (twosided) backvert.append(InstallVertex(id, coords[id], currentBackNormal , currentTexCoord, currentColor)); currentTexCoord = (VECTOR*)NULL; } } void end_face(int, int) { if (skipface || vert.size < 3) return; if (vert.size > 4) { Warning("vrml_importer::end_face", "can't yet handle faces with more than 4 vertices"); return; } PATCH *patch = NULL; if (ccw) patch = PatchCreate(vert.size, vert[0], vert[1], vert[2], vert.size==4 ? vert[3] : (VERTEX *)NULL); else patch = PatchCreate(vert.size, vert[vert.size-1], vert[vert.size-2], vert[vert.size-3], vert.size==4 ? vert[0] : (VERTEX *)NULL); if (!patch) return; // degenerate patches!! currentPatchList = PatchListAdd(currentPatchList, patch); nrfaces++; if (nrfaces%1000 == 0) putc('.', stderr); if (twosided) { PATCH *back = NULL; if (ccw) back = PatchCreate(vert.size, backvert[vert.size-1], backvert[vert.size-2], backvert[vert.size-3], vert.size==4 ? backvert[0] : (VERTEX *)NULL); else back = PatchCreate(vert.size, backvert[0], backvert[1], backvert[2], vert.size==4 ? backvert[3] : (VERTEX *)NULL); if (!back) return; // degenerate patches currentPatchList = PatchListAdd(currentPatchList, back); patch->twin = back; back->twin = patch; nrfaces++; if (nrfaces%1000 == 0) putc('.', stderr); } } void close_geom(void) { GEOM *newgeom = GeomCreate(SurfaceCreate(currentMaterial, currentCoordList, currentNormalList, currentTexCoordList, currentVertexList, currentPatchList, NO_COLORS), SurfaceMethods()); currentGeomList = GeomListAdd(currentGeomList, newgeom); } void end_faces(xrml::Geometry *) { if (currentPatchList) close_geom(); for (int i=0; ipointsize; } void point_color(const xrml::SFColor& col) { RGBSET(currentColor, col.r, col.g, col.b); } void point_normal(const xrml::SFVec3f& norm) { xrml::Vec3 n = xfn * xrml::Vec3(norm); n.normalize(); if (n != xrml::Vec3(0,0,0)) { VECTOR *norm = VectorCreate(n.x, n.y, n.z); currentNormalList = VectorListAdd(currentNormalList, norm); currentNormal = norm; } } void point_texCoord(const xrml::SFVec2f& texco) { xrml::SFVec2f t = texco; xrml::Vec3 T = xrml::Vec3(t.s, t.t, 0.) * txf; VECTOR *texCoord = VectorCreate(T.x, T.y, T.z); currentTexCoordList = VectorListAdd(currentTexCoordList, texCoord); currentTexCoord = texCoord; } void tangentdirs(const xrml::Vec3& n, xrml::Vec3& X, xrml::Vec3& Y) { xrml::Vec3 Z = n; double zz = sqrt(1 - Z.z*Z.z); X = (zz < EPSILON) ? xrml::Vec3(1.,0.,0.) : xrml::Vec3(Z.y/zz, -Z.x/zz, 0.); Y = Z ^ X; } VECTOR* install_point_coord(const xrml::Vec3& c) { VECTOR *coord = VectorCreate(c.x, c.y, c.z); currentCoordList = VectorListAdd(currentCoordList, coord); return coord; } void point_coord(const xrml::SFVec3f& coord) { if (!currentNormal) return; // no normal, no shading xrml::Vec3 C = xrml::Vec3(coord.x, coord.y, coord.z) * xf; // generate a quadrilateral patch xrml::Vec3 n(currentNormal->x, currentNormal->y, currentNormal->z); xrml::Vec3 X, Y; tangentdirs(n, X, Y); X *= currentPointSize*0.5; Y *= currentPointSize*0.5; VERTEX *v[4]; v[0] = InstallVertex(-1, install_point_coord(C+X+Y), currentNormal, currentTexCoord, currentColor); v[1] = InstallVertex(-1, install_point_coord(C-X+Y), currentNormal, currentTexCoord, currentColor); v[2] = InstallVertex(-1, install_point_coord(C-X-Y), currentNormal, currentTexCoord, currentColor); v[3] = InstallVertex(-1, install_point_coord(C+X-Y), currentNormal, currentTexCoord, currentColor); PATCH *patch = PatchCreate(4, v[0], v[1], v[2], v[3]); currentPatchList = PatchListAdd(currentPatchList, patch); currentNormal = NULL; currentTexCoord = NULL; nrfaces++; if (nrfaces%100 == 0) putc('.', stderr); } void end_points(class xrml::PointSet*) { if (currentPatchList) close_geom(); } /////////////////////////////////////////////////////// // Routines for importing PhBRML appearance etc... // extensions to VRML'97 /////////////////////////////////////////////////////// void begin_PhBAppearance(xrml::PhBAppearance *matnode) { char *name = strdup(matnode->name()); matStack.push(currentMaterial); currentMaterial = PhBRMLMaterialCreate(name, matnode, inverse_xf, xf); MaterialLib = MaterialListAdd(MaterialLib, currentMaterial); } void end_PhBAppearance(xrml::PhBAppearance *) { currentMaterial = matStack.pop(); } void phbBackground(xrml::PhBBackground* bkgnode) { if (!Background) Background = PhBRMLBackgroundCreate(bkgnode); } void phbAtmosphere(xrml::PhBAtmosphere*) { xrml::Warning("vrml_importer::phbAtmosphere", "not yet implemented"); } }; void VrmlDefaults(void) { } void ParseVrmlOptions(int *argc, char **argv) { /* ParseOptions(vrmlOptions, argc, argv); */ } void PrintVrmlOptions(FILE *fp) { /* fprintf(fp, "\nVRML input options:\n"); PrintOptions(fp, vrmlOptions); */ } void ReadVrml(char *filename) { xrml::world *w = new xrml::world; if (!w->parse((filename[0] == '#') ? NULL : filename)) return; w->newframe(); fprintf(stderr, "Importing ...\n"); vrml_importer *importer = new vrml_importer; importer->nrQuartCircleDivisions = nqcdivs; w->set_renderer(importer); currentGeomList = GeomListCreate(); currentMaterial = &defaultMaterial; MaterialLib = MaterialListCreate(); nrfaces = 0; w->render(); World = currentGeomList; fprintf(stderr, "\n"); } #endif /*NO_VRML*/