// // Copyright 2021 Pixar // // Licensed under the Apache License, Version 2.0 (the "Apache License") // with the following modification; you may not use this file except in // compliance with the Apache License and the following modification to it: // Section 6. Trademarks. is deleted and replaced with: // // 6. Trademarks. This License does not grant permission to use the trade // names, trademarks, service marks, or product names of the Licensor // and its affiliates, except as required to comply with Section 4(c) of // the License and to reproduce the content of the NOTICE file. // // You may obtain a copy of the Apache License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the Apache License with the above modification is // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the Apache License for the specific // language governing permissions and limitations under the Apache License. // #include "./types.h" #include "./bfrSurfaceEvaluator.h" #include "./farPatchEvaluator.h" #include "../../regression/common/far_utils.h" #include "init_shapes.h" #include "init_shapes_all.h" #include #include #include #include #include using namespace OpenSubdiv; using namespace OpenSubdiv::OPENSUBDIV_VERSION; // // Global set of shapes -- populated by variants of initShapes() that // include explicit lists: // std::vector g_shapes; // // Command line arguments and their parsing: // class Args { public: // options related to testing and reporting: unsigned int posEvaluate : 1; unsigned int d1Evaluate : 1; unsigned int d2Evaluate : 1; unsigned int uvEvaluate : 1; unsigned int posIgnore : 1; unsigned int d1Ignore : 1; unsigned int d2Ignore : 1; unsigned int uvIgnore : 1; unsigned int printArgs : 1; unsigned int printProgress : 1; unsigned int printFaceDiffs : 1; unsigned int printSummary : 1; unsigned int printWarnings : 1; unsigned int ptexConvert : 1; // options affecting configuration and execution: unsigned int evalByStencils : 1; unsigned int doublePrecision : 1; unsigned int noCacheFlag : 1; // options affecting the shape of the limit surface: int depthSharp; int depthSmooth; int bndInterp; int uvInterp; // options related to tessellation and comparison: int uniformRes; float relTolerance; float absTolerance; float uvTolerance; // options affecting the list of shapes to be tested: int shapeCount; Scheme shapeScheme; bool shapesCat2Loop; bool shapesAll; std::vector shapes; // options determining overall success/failure: int passCount; public: Args(int argc, char **argv) : posEvaluate(true), d1Evaluate(false), d2Evaluate(false), uvEvaluate(false), posIgnore(false), d1Ignore(false), d2Ignore(false), uvIgnore(false), printArgs(true), printProgress(true), printFaceDiffs(false), printSummary(true), printWarnings(true), ptexConvert(false), evalByStencils(false), doublePrecision(false), noCacheFlag(false), depthSharp(-1), depthSmooth(-1), bndInterp(-1), uvInterp(-1), uniformRes(3), relTolerance(0.00005f), absTolerance(0.0f), uvTolerance(0.0001f), shapeCount(0), shapeScheme(kCatmark), shapesCat2Loop(false), shapesAll(false), shapes(), passCount(0) { std::string fileString; std::vector shapeNames; for (int i = 1; i < argc; ++i) { char * arg = argv[i]; // Options related to input .obj files: if (strstr(arg, ".obj")) { if (readString(arg, fileString)) { // Use the scheme declared at the time so that multiple // shape/scheme pairs can be specified shapes.push_back( ShapeDesc(arg, fileString.c_str(), shapeScheme)); } else { fprintf(stderr, "Error: Unable to open/read .obj file '%s'\n", arg); exit(0); } // Options affecting the limit surface shapes: } else if (!strcmp(arg, "-l")) { if (++i < argc) { int maxLevel = atoi(argv[i]); depthSharp = maxLevel; depthSmooth = maxLevel; } } else if (!strcmp(arg, "-lsharp")) { if (++i < argc) depthSharp = atoi(argv[i]); } else if (!strcmp(arg, "-lsmooth")) { if (++i < argc) depthSmooth = atoi(argv[i]); } else if (!strcmp(argv[i], "-bint")) { if (++i < argc) bndInterp = atoi(argv[i]); } else if (!strcmp(argv[i], "-uvint")) { if (++i < argc) uvInterp = atoi(argv[i]); // Options affecting what gets evaluated: } else if (!strcmp(arg, "-res")) { if (++i < argc) uniformRes = atoi(argv[i]); } else if (!strcmp(arg, "-pos")) { posEvaluate = true; } else if (!strcmp(arg, "-nopos")) { posEvaluate = false; } else if (!strcmp(arg, "-d1")) { d1Evaluate = true; } else if (!strcmp(arg, "-nod1")) { d1Evaluate = false; } else if (!strcmp(arg, "-d2")) { d2Evaluate = true; } else if (!strcmp(arg, "-nod2")) { d2Evaluate = false; } else if (!strcmp(arg, "-uv")) { uvEvaluate = true; } else if (!strcmp(arg, "-nouv")) { uvEvaluate = false; } else if (!strcmp(arg, "-ptex")) { ptexConvert = true; } else if (!strcmp(arg, "-noptex")) { ptexConvert = false; // Options affecting what gets compared and reported: } else if (!strcmp(arg, "-skippos")) { posIgnore = true; } else if (!strcmp(arg, "-skipd1")) { d1Ignore = true; } else if (!strcmp(arg, "-skipd2")) { d2Ignore = true; } else if (!strcmp(arg, "-skipuv")) { uvIgnore = true; } else if (!strcmp(arg, "-faces")) { printFaceDiffs = true; // Options affecting comparison tolerances: } else if (!strcmp(argv[i], "-reltol")) { if (++i < argc) relTolerance = (float)atof(argv[i]); } else if (!strcmp(argv[i], "-abstol")) { if (++i < argc) absTolerance = (float)atof(argv[i]); } else if (!strcmp(argv[i], "-uvtol")) { if (++i < argc) uvTolerance = (float)atof(argv[i]); // Options controlling other internal processing: } else if (!strcmp(arg, "-stencils")) { evalByStencils = true; } else if (!strcmp(arg, "-double")) { doublePrecision = true; } else if (!strcmp(arg, "-nocache")) { noCacheFlag = true; // Options affecting the shapes to be included: } else if (!strcmp(arg, "-bilinear")) { shapeScheme = kBilinear; } else if (!strcmp(arg, "-catmark")) { shapeScheme = kCatmark; } else if (!strcmp(arg, "-loop")) { shapeScheme = kLoop; } else if (!strcmp(arg, "-cat2loop")) { shapesCat2Loop = true; } else if (!strcmp(arg, "-count")) { if (++i < argc) shapeCount = atoi(argv[i]); } else if (!strcmp(arg, "-shape")) { if (++i < argc) { shapeNames.push_back(std::string(argv[i])); } } else if (!strcmp(arg, "-all")) { shapesAll = true; // Printing and reporting: } else if (!strcmp(arg, "-args")) { printArgs = true; } else if (!strcmp(arg, "-noargs")) { printArgs = false; } else if (!strcmp(arg, "-prog")) { printProgress = true; } else if (!strcmp(arg, "-noprog")) { printProgress = false; } else if (!strcmp(arg, "-sum")) { printSummary = true; } else if (!strcmp(arg, "-nosum")) { printSummary = false; } else if (!strcmp(arg, "-quiet")) { printWarnings = false; } else if (!strcmp(arg, "-silent")) { printArgs = false; printProgress = false; printSummary = false; printWarnings = false; // Success/failure of the entire test: } else if (!strcmp(argv[i], "-pass")) { if (++i < argc) passCount = atoi(argv[i]); // Unrecognized... } else { fprintf(stderr, "Error: Unrecognized argument '%s'\n", arg); exit(0); } } // Validation -- possible conflicting options, values, etc. if (bndInterp > 2) { fprintf(stderr, "Warning: Ignoring bad value to -bint (%d)\n", bndInterp); bndInterp = -1; } if (uvInterp > 5) { fprintf(stderr, "Warning: Ignoring bad value to -uvint (%d)\n", uvInterp); uvInterp = -1; } if (d2Evaluate) { if (!d1Evaluate) { fprintf(stderr, "Warning: 2nd deriv evaluation forces 1st.\n"); d1Evaluate = true; } if (!posEvaluate) { fprintf(stderr, "Warning: 2nd deriv evaluation forces pos.\n"); posEvaluate = true; } } else if (d1Evaluate) { if (!posEvaluate) { fprintf(stderr, "Warning: 1st deriv evaluation forces pos.\n"); posEvaluate = true; } } if (!posEvaluate && !uvEvaluate) { fprintf(stderr, "Error: All pos and UV evaluation disabled.\n"); exit(0); } if (posIgnore && d1Ignore && d2Ignore && uvIgnore) { fprintf(stderr, "Error: All pos and UV comparisons disabled.\n"); exit(0); } if ((depthSmooth == 0) || (depthSharp == 0)) { fprintf(stderr, "Warning: Far evaluation unstable with refinement level 0.\n"); } // Managing the list of shapes: assert(g_shapes.empty()); if (!shapeNames.empty()) { if (shapesAll) { initShapesAll(g_shapes); } else { initShapes(g_shapes); } // Maybe worth building a map -- for this and more... for (size_t i = 0; i < shapeNames.size(); ++i) { std::string & shapeName = shapeNames[i]; bool found = false; for (size_t j = 0; !found && (j < g_shapes.size()); ++j) { if (g_shapes[j].name == shapeName) { shapes.push_back(g_shapes[j]); found = true; break; } } if (!found) { fprintf(stderr, "Error: Specified shape '%s' not found.\n", shapeName.c_str()); exit(0); } } } } ~Args() { } void Print() const { char const * boolStrings[2] = { "false", "true" }; char const * bIntStrings[3] = { "BOUNDARY_NONE", "BOUNDARY_EDGE_ONLY", "BOUNDARY_EDGE_AND_CORNER" }; char const * fvIntStrings[6] = { "LINEAR_NONE", "LINEAR_CORNERS_ONLY", "LINEAR_CORNERS_PLUS1", "LINEAR_CORNERS_PLUS2", "LINEAR_BOUNDARIES", "LINEAR_ALL" }; printf("\n"); printf("Shape options:\n"); if (depthSharp >= 0) { printf(" - max level sharp = %d\n", depthSharp); } else { printf(" - max level sharp = %d (dflt)\n", (Bfr::SurfaceFactory::Options()).GetApproxLevelSharp()); } if (depthSmooth >= 0) { printf(" - max level smooth = %d\n", depthSmooth); } else { printf(" - max level smooth = %d (dflt)\n", (Bfr::SurfaceFactory::Options()).GetApproxLevelSmooth()); } if (bndInterp < 0) { printf(" - boundary interp = (as assigned)\n"); } else { printf(" - boundary interp = %s\n", bIntStrings[bndInterp]); } if (uvEvaluate) { if (uvInterp < 0) { printf(" - UV linear interp = (as assigned)\n"); } else { printf(" - UV linear interp = %s\n", fvIntStrings[uvInterp]); } } printf("Evaluation options:\n"); printf(" - tessellation res = %d\n", uniformRes); printf(" - position = %s\n", boolStrings[posEvaluate]); printf(" - 1st derivative = %s\n", boolStrings[d1Evaluate]); printf(" - 2nd derivative = %s\n", boolStrings[d2Evaluate]); printf(" - UV = %s\n", boolStrings[uvEvaluate]); printf("Comparison options:\n"); if (absTolerance > 0.0f) { printf(" - tolerance (abs) = %g\n", absTolerance); } else { printf(" - tolerance (rel) = %g\n", relTolerance); } if (uvEvaluate) { printf(" - tolerance UV = %g\n", uvTolerance); } if (posEvaluate && posIgnore) { printf(" - ignore pos = %s\n", boolStrings[posIgnore]); } if (d1Evaluate && d1Ignore) { printf(" - ignore 1st deriv = %s\n", boolStrings[d1Ignore]); } if (d2Evaluate && d2Ignore) { printf(" - ignore 2nd deriv = %s\n", boolStrings[d2Ignore]); } if (uvEvaluate && uvIgnore) { printf(" - ignore UV = %s\n", boolStrings[uvIgnore]); } printf("\n"); } private: Args() { } bool readString(const char *fileName, std::string& fileString) { std::ifstream ifs(fileName); if (ifs) { std::stringstream ss; ss << ifs.rdbuf(); ifs.close(); fileString = ss.str(); return true; } return false; } }; // // Create a TopologyRefiner from a Shape: // template Far::TopologyRefiner * createTopologyRefiner(ShapeDesc const & shapeDesc, std::vector< Vec3 > & shapePos, std::vector< Vec3 > & shapeUVs, Args const & args) { typedef Vec3 Vec3Real; // // Load the Shape -- skip with a warning on failure: // Shape * shape = Shape::parseObj(shapeDesc.data.c_str(), shapeDesc.scheme, shapeDesc.isLeftHanded); if (shape == 0) { if (args.printWarnings) { fprintf(stderr, "Warning: Failed to parse shape '%s'\n", shapeDesc.name.c_str()); } return 0; } // Verify UVs before continuing: if (args.uvEvaluate) { if (shape->uvs.empty() != shape->faceuvs.empty()) { if (args.printWarnings) { fprintf(stderr, "Warning: Incomplete UVs assigned to Shape '%s'\n", shapeDesc.name.c_str()); } delete shape; return 0; } } // // Create a TopologyRefiner and load position and UVs: // Sdc::SchemeType sdcType = GetSdcType(*shape); if (args.shapesCat2Loop && (sdcType == Sdc::SCHEME_LOOP)) { if (args.printWarnings) { fprintf(stderr, "\t\tWarning: Applying Catmark to Loop shape '%s'\n", shapeDesc.name.c_str()); } sdcType = Sdc::SCHEME_CATMARK; } Sdc::Options sdcOptions = GetSdcOptions(*shape); if (args.bndInterp >= 0) { sdcOptions.SetVtxBoundaryInterpolation( (Sdc::Options::VtxBoundaryInterpolation) args.bndInterp); } if (args.uvInterp >= 0) { sdcOptions.SetFVarLinearInterpolation( (Sdc::Options::FVarLinearInterpolation) args.uvInterp); } Far::TopologyRefiner * refiner = Far::TopologyRefinerFactory::Create(*shape, Far::TopologyRefinerFactory::Options(sdcType, sdcOptions)); if (refiner == 0) { if (args.printWarnings) { fprintf(stderr, "Warning: Unable to interpret Shape '%s'\n", shapeDesc.name.c_str()); } delete shape; return 0; } int numVertices = refiner->GetNumVerticesTotal(); shapePos.resize(numVertices); for (int i = 0; i < numVertices; ++i) { shapePos[i] = Vec3Real(shape->verts[i*3], shape->verts[i*3+1], shape->verts[i*3+2]); } shapeUVs.resize(0); if (args.uvEvaluate) { if (refiner->GetNumFVarChannels()) { int numUVs = refiner->GetNumFVarValuesTotal(0); shapeUVs.resize(numUVs); for (int i = 0; i < numUVs; ++i) { shapeUVs[i] = Vec3Real(shape->uvs[i*2], shape->uvs[i*2+1], 0.0f); } } } delete shape; return refiner; } // // Compute the bounding box of a Vec3 vector and a relative tolerance: // template REAL GetRelativeTolerance(std::vector< Vec3 > const & p, REAL fraction) { Vec3 pMin = p[0]; Vec3 pMax = p[0]; for (size_t i = 1; i < p.size(); ++i) { Vec3 const & pi = p[i]; pMin[0] = std::min(pMin[0], pi[0]); pMin[1] = std::min(pMin[1], pi[1]); pMin[2] = std::min(pMin[2], pi[2]); pMax[0] = std::max(pMax[0], pi[0]); pMax[1] = std::max(pMax[1], pi[1]); pMax[2] = std::max(pMax[2], pi[2]); } Vec3 pDelta = pMax - pMin; REAL maxSize = std::max(std::abs(pDelta[0]), std::abs(pDelta[1])); maxSize = std::max(maxSize, std::abs(pDelta[2])); return fraction * maxSize; } // // An independent test from limit surface evaluation: comparing the // conversion of (u,v) coordinates for Bfr::Parameterization to Ptex // and back (subject to a given tolerance): // template void ValidatePtexConversion(Bfr::Parameterization const & param, REAL const givenCoord[2], REAL tol = 0.0001f) { if (!param.HasSubFaces()) return; // // Convert the given (u,v) coordinate to Ptex and back and // compare the final result to the original: // REAL ptexCoord[2]; REAL finalCoord[2]; int ptexFace = param.ConvertCoordToNormalizedSubFace(givenCoord, ptexCoord); param.ConvertNormalizedSubFaceToCoord(ptexFace, ptexCoord, finalCoord); bool subFaceDiff = (ptexFace != param.GetSubFace(givenCoord)); bool uCoordDiff = (std::abs(finalCoord[0] - givenCoord[0]) > tol); bool vCoordDiff = (std::abs(finalCoord[1] - givenCoord[1]) > tol); if (subFaceDiff || uCoordDiff || vCoordDiff) { fprintf(stderr, "Warning: Mismatch in sub-face Parameterization conversion:\n"); if (subFaceDiff ) { fprintf(stderr, " converted sub-face (%d) != original (%d)\n", ptexFace, param.GetSubFace(givenCoord)); } if (uCoordDiff || vCoordDiff) { fprintf(stderr, " converted coord (%f,%f) != original (%f,%f)\n", finalCoord[0], finalCoord[1], givenCoord[0], givenCoord[1]); } } } // // Compare two meshes using Bfr::Surfaces and a Far::PatchTable: // template int testMesh(Far::TopologyRefiner const & mesh, std::string const & meshName, std::vector< Vec3 > const & meshPos, std::vector< Vec3 > const & meshUVs, Args const & args) { // // Determine what to evaluate/compare based on args and mesh content // (remember that these are not completely independent -- position // evaluation will have been set if evaluating any derivatives): // bool evalPos = args.posEvaluate; bool evalD1 = args.d1Evaluate; bool evalD2 = args.d2Evaluate; bool evalUV = args.uvEvaluate && (meshUVs.size() > 0); bool comparePos = evalPos && !args.posIgnore; bool compareD1 = evalD1 && !args.d1Ignore; bool compareD2 = evalD2 && !args.d2Ignore; bool compareUV = evalUV && !args.uvIgnore; // If nothing to compare, return 0 failures: if ((comparePos + compareD1 + compareD2 + compareUV) == 0) { return 0; } // Declare/allocate output evaluation buffers for both Bfr and Far: std::vector evalCoords; EvalResults bfrResults; bfrResults.evalPosition = evalPos; bfrResults.eval1stDeriv = evalD1; bfrResults.eval2ndDeriv = evalD2; bfrResults.evalUV = evalUV; bfrResults.useStencils = args.evalByStencils; EvalResults farResults; farResults.evalPosition = evalPos; farResults.eval1stDeriv = evalD1; farResults.eval2ndDeriv = evalD2; farResults.evalUV = evalUV; // // Create evaluators for Bfr and Far -- using the same set of Bfr // options to ensure consistency (the Far evaluator needs to interpret // them appropriate to Far::PatchTable and associated refinement) // Bfr::SurfaceFactory::Options surfaceOptions; // Leave approximation defaults in place unless explicitly overridden: if (args.depthSharp >= 0) { surfaceOptions.SetApproxLevelSharp(args.depthSharp); } if (args.depthSmooth >= 0) { surfaceOptions.SetApproxLevelSmooth(args.depthSmooth); } surfaceOptions.SetDefaultFVarID(0); surfaceOptions.EnableCaching(!args.noCacheFlag); BfrSurfaceEvaluator bfrEval(mesh, meshPos, meshUVs, surfaceOptions); FarPatchEvaluator farEval(mesh, meshPos, meshUVs, surfaceOptions); // // Initialize tolerances and variables to track differences: // REAL pTol = (args.absTolerance > 0.0f) ? args.absTolerance : GetRelativeTolerance(meshPos, args.relTolerance); REAL d1Tol = pTol * 5.0f; REAL d2Tol = d1Tol * 5.0f; REAL uvTol = args.uvTolerance; VectorDelta pDelta(pTol); VectorDelta duDelta(d1Tol); VectorDelta dvDelta(d1Tol); VectorDelta duuDelta(d2Tol); VectorDelta duvDelta(d2Tol); VectorDelta dvvDelta(d2Tol); VectorDelta uvDelta(uvTol); FaceDelta faceDelta; MeshDelta meshDelta; bool meshHasBeenLabeled = false; int numFaces = mesh.GetNumFacesTotal(); for (int faceIndex = 0; faceIndex < numFaces; ++faceIndex) { // // Make sure both match in terms of identifying a limit surface: // assert(bfrEval.FaceHasLimit(faceIndex) == farEval.FaceHasLimit(faceIndex)); if (!farEval.FaceHasLimit(faceIndex)) continue; // // Declare/define a Tessellation to generate a consistent set of // (u,v) locations to compare and evaluate: // int faceSize = mesh.GetLevel(0).GetFaceVertices(faceIndex).size(); Bfr::Parameterization faceParam(mesh.GetSchemeType(), faceSize); assert(faceParam.IsValid()); Bfr::Tessellation faceTess(faceParam, args.uniformRes); assert(faceTess.IsValid()); evalCoords.resize(2 * faceTess.GetNumCoords()); faceTess.GetCoords(&evalCoords[0]); // // Before evaluating and comparing, run the test to convert the // parametric coords to Ptex and back: // if (args.ptexConvert) { for (int i = 0; i < faceTess.GetNumCoords(); ++i) { ValidatePtexConversion(faceParam, &evalCoords[2*i]); } } // // Evaluate and capture results of comparisons between results: // bfrEval.Evaluate(faceIndex, evalCoords, bfrResults); farEval.Evaluate(faceIndex, evalCoords, farResults); if (comparePos) { pDelta.Compare(bfrResults.p, farResults.p); } if (compareD1) { duDelta.Compare(bfrResults.du, farResults.du); dvDelta.Compare(bfrResults.dv, farResults.dv); } if (compareD2) { duuDelta.Compare(bfrResults.duu, farResults.duu); duvDelta.Compare(bfrResults.duv, farResults.duv); dvvDelta.Compare(bfrResults.dvv, farResults.dvv); } if (compareUV) { uvDelta.Compare(bfrResults.uv, farResults.uv); } // // Note collective differences for this face and report: // faceDelta.Clear(); faceDelta.AddPDelta(pDelta); faceDelta.AddDuDelta(duDelta); faceDelta.AddDvDelta(dvDelta); faceDelta.AddDuuDelta(duuDelta); faceDelta.AddDuvDelta(duvDelta); faceDelta.AddDvvDelta(dvvDelta); faceDelta.AddUVDelta(uvDelta); if (args.printFaceDiffs && faceDelta.hasDeltas) { if (!meshHasBeenLabeled) { meshHasBeenLabeled = true; printf("'%s':\n", meshName.c_str()); } printf("\t Face %d:\n", faceIndex); if (comparePos && faceDelta.numPDeltas) { printf("\t\t POS:%6d diffs, max delta P = %g\n", faceDelta.numPDeltas, (float) faceDelta.maxPDelta); } if (compareD1 && faceDelta.numD1Deltas) { printf("\t\t D1:%6d diffs, max delta D1 = %g\n", faceDelta.numD1Deltas, (float) faceDelta.maxD1Delta); } if (compareD2 && faceDelta.numD2Deltas) { printf("\t\t D2:%6d diffs, max delta D2 = %g\n", faceDelta.numD2Deltas, (float) faceDelta.maxD2Delta); } if (compareUV && faceDelta.hasUVDeltas) { printf("\t\t UV:%6d diffs, max delta UV = %g\n", uvDelta.numDeltas, (float) uvDelta.maxDelta); } } // Add the results for this face to the collective mesh delta: meshDelta.AddFace(faceDelta); } // // Report the differences for this mesh: // if (meshDelta.numFacesWithDeltas) { if (args.printFaceDiffs) { printf("\t Total:\n"); } else { printf("'%s':\n", meshName.c_str()); } } if (comparePos && meshDelta.numFacesWithPDeltas) { printf("\t\tPOS diffs:%6d faces, max delta P = %g\n", meshDelta.numFacesWithPDeltas, (float) meshDelta.maxPDelta); } if (compareD1 && meshDelta.numFacesWithD1Deltas) { printf("\t\t D1 diffs:%6d faces, max delta D1 = %g\n", meshDelta.numFacesWithD1Deltas, (float) meshDelta.maxD1Delta); } if (compareD2 && meshDelta.numFacesWithD2Deltas) { printf("\t\t D2 diffs:%6d faces, max delta D2 = %g\n", meshDelta.numFacesWithD2Deltas, (float) meshDelta.maxD2Delta); } if (compareUV && meshDelta.numFacesWithUVDeltas) { printf("\t\t UV diffs:%6d faces, max delta UV = %g\n", meshDelta.numFacesWithUVDeltas, (float) meshDelta.maxUVDelta); } return meshDelta.numFacesWithDeltas; } // // Run the comparison for a given Shape in single or double precision: // template int testShape(ShapeDesc const & shapeDesc, Args const & args) { // // Get the TopologyRefiner, positions and UVs for the Shape, report // failure to generate the shape, and run the test: // std::string const & meshName = shapeDesc.name; std::vector< Vec3 > basePos; std::vector< Vec3 > baseUV; Far::TopologyRefiner * refiner = createTopologyRefiner(shapeDesc, basePos, baseUV, args); if (refiner == 0) { if (args.printWarnings) { fprintf(stderr, "Warning: Shape '%s' ignored (unable to construct refiner)\n", meshName.c_str()); } return -1; } int nFailures = testMesh(*refiner, meshName, basePos, baseUV, args); delete refiner; return nFailures; } // // Run comparison tests on a list of shapes using command line options: // int main(int argc, char **argv) { Args args(argc, argv); // Capture relevant command line options used here: if (args.printArgs) { args.Print(); } // // Initialize the list of shapes and test each (or only the first): // // - currently the internal list can be overridden on the command // line (so use of wildcards is possible) // // - still exploring additional command line options, e.g. hoping // to specify a list of shape names from the internal list... // // So a bit more to be done here... // std::vector& shapeList = g_shapes; if (!args.shapes.empty()) { shapeList.swap(args.shapes); } if (shapeList.empty()) { if (args.shapesAll) { initShapesAll(shapeList); } else { initShapes(shapeList); } } int shapesToTest = (int) shapeList.size(); int shapesIgnored = 0; if ((args.shapeCount > 0) && (args.shapeCount < shapesToTest)) { shapesIgnored = shapesToTest - args.shapeCount; shapesToTest = args.shapeCount; } if (args.printProgress) { printf("Testing %d shapes", shapesToTest); if (shapesIgnored) { printf(" (%d ignored)", shapesIgnored); } printf(":\n"); } // // Run the comparison test for each shape (ShapeDesc) in the // specified precision and report results: // int shapesFailed = 0; for (int shapeIndex = 0; shapeIndex < shapesToTest; ++shapeIndex) { ShapeDesc & shapeDesc = shapeList[shapeIndex]; if (args.printProgress) { printf("%4d of %d: '%s'\n", 1 + shapeIndex, shapesToTest, shapeDesc.name.c_str()); } int nFailures = args.doublePrecision ? testShape(shapeDesc, args) : testShape(shapeDesc, args); if (nFailures < 0) { // Possible error/warning...? ++ shapesFailed; } if (nFailures > 0) { ++ shapesFailed; } } if (args.printSummary) { printf("\n"); if (shapesFailed == 0) { printf("All tests passed for %d shapes\n", shapesToTest); } else { printf("Total failures: %d of %d shapes\n", shapesFailed, shapesToTest); } } return (shapesFailed == args.passCount) ? EXIT_SUCCESS : EXIT_FAILURE; }