/* vrml.c: saves "illuminated" model in the VRML'97 file */ #include "defaults.h" #include "mcradP.h" #include "hierarchy.h" #include "writevrml.h" #include "vrml.h" #include "render.h" #include "geom.h" #include "scene.h" #define FACES_PER_SET 1000 /* keeps all arrays smaller than 30k els */ static int vid, nwrit, matidx; static int nrcoords, nrcolors, nrcoordindices, pass; static FILE *vrmlfp; /* iterator adapted in order to handle only a subset of the elements * at a time, taking into account 'pass' and FACES_PER_SET */ static void (*elemfunc)(ELEMENT*); static int leaf_element_count; static void count_and_call(ELEMENT *elem) { if (leaf_element_count >= pass * FACES_PER_SET && leaf_element_count < (pass+1) * FACES_PER_SET) elemfunc(elem); leaf_element_count++; } static void GeomIterateLeafElements(GEOM *geom, void (*func)(ELEMENT*)) { PATCHLIST *patches = GeomPatchList(geom); elemfunc = func; leaf_element_count = 0; ForAllPatches(P, patches) { ForAllLeafElements(TOPLEVEL_ELEMENT(P), count_and_call); } EndForAll; } static void ResetVertexId(VERTEX *v) { v->tmp = -1; } static void TriangleResetVertexIds(VERTEX *v1, VERTEX *v2, VERTEX *v3) { ResetVertexId(v1); ResetVertexId(v2); ResetVertexId(v3); } static void QuadResetVertexIds(VERTEX *v1, VERTEX *v2, VERTEX *v3, VERTEX *v4) { ResetVertexId(v1); ResetVertexId(v2); ResetVertexId(v3); ResetVertexId(v4); } /* with T-vertex elimination */ static void ResetVertexIds(ELEMENT *elem) { ElementTVertexElimination(elem, TriangleResetVertexIds, QuadResetVertexIds); } /* without T-vertex elimination: this should do, but it doesn't do in * practice. */ static void ElementResetVertexIds(ELEMENT *elem) { int i; for (i=0; inrvertices; i++) elem->vertex[i]->tmp = -1; } static void WriteVertexCoord(VERTEX *v) { if (v->tmp == -1) { /* not yet written */ if (nwrit>0) fprintf(vrmlfp, ", "); nwrit++; if (nwrit%4 == 0) fprintf(vrmlfp, "\n\t "); fprintf(vrmlfp, "%g %g %g", v->point->x, v->point->y, v->point->z); v->tmp = vid; vid++; } } static void TriangleWriteVertexCoords(VERTEX *v1, VERTEX *v2, VERTEX *v3) { WriteVertexCoord(v1); WriteVertexCoord(v2); WriteVertexCoord(v3); } static void QuadWriteVertexCoords(VERTEX *v1, VERTEX *v2, VERTEX *v3, VERTEX *v4) { WriteVertexCoord(v1); WriteVertexCoord(v2); WriteVertexCoord(v3); WriteVertexCoord(v4); } static void WriteVertexCoords(ELEMENT *elem) { ElementTVertexElimination(elem, TriangleWriteVertexCoords, QuadWriteVertexCoords); } static void WriteCoords(void) { ForAllLeafElements(hierarchy.topcluster, ElementResetVertexIds); vid = nwrit = 0; fprintf(vrmlfp, "\tcoord Coordinate {\n\t point [ "); ForAllLeafElements(hierarchy.topcluster, WriteVertexCoords); fprintf(vrmlfp, " ] "); fprintf(vrmlfp, "\n\t}\n"); nrcoords = nwrit; } static void WritePrimitiveCoords(GEOM *geom) { GeomIterateLeafElements(geom, ResetVertexIds); vid = nwrit = 0; fprintf(vrmlfp, "\tcoord Coordinate {\n\t point [ "); GeomIterateLeafElements(geom, WriteVertexCoords); fprintf(vrmlfp, " ] "); fprintf(vrmlfp, "\n\t}\n"); nrcoords = nwrit; } static void WriteVertexColor(VERTEX *v) { if (v->tmp == -1) { /* not yet written */ if (nwrit>0) fprintf(vrmlfp, ", "); nwrit++; if (nwrit%4 == 0) fprintf(vrmlfp, "\n\t "); fprintf(vrmlfp, "%.3g %.3g %.3g", v->color.r, v->color.g, v->color.b); v->tmp = vid; vid++; } } static void TriangleWriteVertexColors(VERTEX *v1, VERTEX *v2, VERTEX *v3) { WriteVertexColor(v1); WriteVertexColor(v2); WriteVertexColor(v3); } static void QuadWriteVertexColors(VERTEX *v1, VERTEX *v2, VERTEX *v3, VERTEX *v4) { WriteVertexColor(v1); WriteVertexColor(v2); WriteVertexColor(v3); WriteVertexColor(v4); } static void ElementWriteVertexColors(ELEMENT *elem) { ElementTVertexElimination(elem, TriangleWriteVertexColors, QuadWriteVertexColors); } static void WriteVertexColors(void) { ForAllLeafElements(hierarchy.topcluster, ResetVertexIds); vid = nwrit = 0; fprintf(vrmlfp, "\tcolor Color {\n\t color [ "); ForAllLeafElements(hierarchy.topcluster, ElementWriteVertexColors); fprintf(vrmlfp, " ] "); fprintf(vrmlfp, "\n\t}\n"); nrcolors = nwrit; } static void WritePrimitiveColors(GEOM *geom) { GeomIterateLeafElements(geom, ResetVertexIds); vid = nwrit = 0; fprintf(vrmlfp, "\tcolor Color {\n\t color [ "); GeomIterateLeafElements(geom, ElementWriteVertexColors); fprintf(vrmlfp, " ] "); fprintf(vrmlfp, "\n\t}\n"); nrcolors = nwrit; } static void WriteColors(void) { static int wgiv=FALSE; if (!renderopts.smooth_shading && !wgiv) { Warning(NULL, "I assume you want a smooth shaded model ..."); wgiv = TRUE; } fprintf(vrmlfp, "\tcolorPerVertex %s\n", "TRUE"); WriteVertexColors(); } static void WriteCoordIndex(int index) { nwrit++; if (nwrit%20 == 0) fprintf(vrmlfp, "\n\t "); fprintf(vrmlfp, "%d ", index); } static void TriangleWriteVertexCoordIndices(VERTEX *v1, VERTEX *v2, VERTEX *v3) { WriteCoordIndex(v1->tmp); WriteCoordIndex(v2->tmp); WriteCoordIndex(v3->tmp); WriteCoordIndex(-1); } static void QuadWriteVertexCoordIndices(VERTEX *v1, VERTEX *v2, VERTEX *v3, VERTEX *v4) { WriteCoordIndex(v1->tmp); WriteCoordIndex(v2->tmp); WriteCoordIndex(v3->tmp); WriteCoordIndex(v4->tmp); WriteCoordIndex(-1); } static void ElementWriteCoordIndices(ELEMENT *elem) { ElementTVertexElimination(elem, TriangleWriteVertexCoordIndices, QuadWriteVertexCoordIndices); } static void WriteCoordIndices(void) { nwrit = 0; fprintf(vrmlfp, "\tcoordIndex [ "); ForAllLeafElements(hierarchy.topcluster, ElementWriteCoordIndices); fprintf(vrmlfp, " ]\n"); nrcoordindices = nwrit; } static void WritePrimitiveCoordIndices(GEOM *geom) { nwrit = 0; fprintf(vrmlfp, "\tcoordIndex [ "); GeomIterateLeafElements(geom, ElementWriteCoordIndices); fprintf(vrmlfp, " ]\n"); nrcoordindices = nwrit; } void McrWriteVRMLHeader(FILE *fp) { TRANSFORM model_xf; VECTOR model_rotaxis; float model_rotangle; fprintf(fp, "#VRML V2.0 utf8\n\n"); fprintf(fp, "WorldInfo {\n title \"%s\"\n info [ \"Created with RenderPark (%s) using Monte Carlo radiosty\" ]\n}\n\n", "Some nice model", RPKHOME); fprintf(fp, "NavigationInfo {\n type \"WALK\"\n headlight FALSE\n}\n\n"); model_xf = VRMLModelTransform(&model_rotaxis, &model_rotangle); WriteVRMLViewPoints(fp, model_xf); fprintf(fp, "Transform {\n rotation %g %g %g %g\n children [\n", model_rotaxis.x, model_rotaxis.y, model_rotaxis.z, model_rotangle); } void McrWriteVRMLTrailer(FILE *fp) { fprintf(fp, " ]\n}\n\n"); } static unsigned char IdFirstChar[256], IdRestChar[256]; static void initidtranstabs(void) { int i; char *IdSpecialChars = "!$%&()*/:;<=>?@^_`|~"; for (i=0; i<256; i++) IdFirstChar[i] = '_'; for (i='a'; i<='z'; i++) IdFirstChar[i] = i; for (i='A'; i<='Z'; i++) IdFirstChar[i] = i; for (i=0; i 100) { Warning("make_valid_vrml_id", "id '%s' is being truncated to %d characters", id, 100); } n = idlen > 100 ? 100 : idlen; /* minimum of both */ buf[0] = IdFirstChar[(int)id[0]]; for (i=1; imaterial; PATCH *first_patch = (surf->faces) ? surf->faces->patch : (PATCH*)NULL; HITREC hit; COLOR Rd, Rs; RGB rd, rs; float specularity; if (!first_patch || !mat || !mat->bsdf) return; if (mat->radiance_data != NULL) { /* has been written before */ fprintf(vrmlfp, " appearance Appearance {\n"); fprintf(vrmlfp, "\tmaterial USE %s\n", make_valid_vrml_id((char*)mat->radiance_data)); fprintf(vrmlfp, " }\n"); return; } InitHit(&hit, first_patch, (GEOM*)NULL, &first_patch->midpoint, &first_patch->normal, mat, 0.); Rd = BsdfScatteredPower(mat->bsdf, &hit, &first_patch->normal, BRDF_DIFFUSE_COMPONENT); ColorToRGB(Rd, &rd); Rs = BsdfScatteredPower(mat->bsdf, &hit, &first_patch->normal, BRDF_GLOSSY_COMPONENT|BRDF_SPECULAR_COMPONENT); ColorToRGB(Rs, &rs); specularity = 128.; fprintf(vrmlfp, " appearance Appearance {\n"); fprintf(vrmlfp, "\tmaterial DEF %s Material {\n", make_valid_vrml_id(mat->name)); fprintf(vrmlfp, "\t ambientIntensity 0.\n"); if (mat->edf) fprintf(vrmlfp, "\t emissiveColor %.3g %.3g %.3g\n", rd.r, rd.g, rd.b); else fprintf(vrmlfp, "\t emissiveColor %.3g %.3g %.3g\n", 0., 0., 0.); fprintf(vrmlfp, "\t diffuseColor %.3g %.3g %.3g \n", rd.r, rd.g, rd.b); fprintf(vrmlfp, "\t specularColor %.3g %.3g %.3g \n", rs.r, rs.g, rs.b); fprintf(vrmlfp, "\t shininess %g\n", specularity/128. > 1. ? 1. : specularity/128.); fprintf(vrmlfp, "\t transparency 0.\n"); fprintf(vrmlfp, "\t}\n"); fprintf(vrmlfp, " }\n"); mat->radiance_data = mat->name; } static void BeginWritePrimitive(GEOM *geom) { static int wgiv=FALSE; fprintf(vrmlfp, " Shape {\n"); if (GeomIsSurface(geom)) WriteMaterial(geom); fprintf(vrmlfp, " geometry IndexedFaceSet {\n"); if (GeomIsSurface(geom)) fprintf(vrmlfp, "\tsolid %s\n", GeomGetSurface(geom)->material->sided ? "TRUE" : "FALSE"); if (!renderopts.smooth_shading && !wgiv) { Warning(NULL, "I assume you want a smooth shaded model ..."); wgiv = TRUE; } fprintf(vrmlfp, "\tcolorPerVertex %s\n", "TRUE"); } static char *PrimitiveMatName(GEOM *geom) { SURFACE *surf = GeomGetSurface(geom); if (!surf) return "unknown (not a surface)"; else return make_valid_vrml_id(surf->material->name); } static void EndWritePrimitive(GEOM *geom) { fprintf(vrmlfp, " }\n"); /* end IndexedFaceSet */ fprintf(vrmlfp, " },\n"); /* end Shape */ fprintf(stderr, "Shape material %s, pass %d, %d coords, %d colors, %d coordindices\n", PrimitiveMatName(geom), pass, nrcoords, nrcolors, nrcoordindices); } static void WritePrimitivePass(GEOM *geom) { BeginWritePrimitive(geom); WritePrimitiveCoords(geom); WritePrimitiveColors(geom); WritePrimitiveCoordIndices(geom); EndWritePrimitive(geom); } static void WritePrimitive(GEOM *geom) { pass = 0; WritePrimitivePass(geom); if (leaf_element_count > FACES_PER_SET) { /* large set, additional passes needed */ for (pass=1; pass <= leaf_element_count/FACES_PER_SET; pass++) WritePrimitivePass(geom); /* write next batch of leaf elements */ } } void IteratePrimitiveGeoms(GEOMLIST *list, void (*func)(GEOM*)) { ForAllGeoms(geom, list) { if (GeomIsAggregate(geom)) IteratePrimitiveGeoms(GeomPrimList(geom), func); else func(geom); } EndForAll; } static void ResetMaterialData(void) { ForAllMaterials(mat, MaterialLib) { mat->radiance_data = (void*)NULL; } EndForAll; } void McrWriteVRML(FILE *fp) { initidtranstabs(); matidx = 0; ResetMaterialData(); McrWriteVRMLHeader(fp); vrmlfp = fp; IteratePrimitiveGeoms(World, WritePrimitive); #ifdef NEVER WriteCoords(); WriteColors(); WriteCoordIndices(); #endif McrWriteVRMLTrailer(fp); ResetMaterialData(); }