// ================================================================ // $Id: environ.cpp,v 1.1 2004/02/16 14:45:59 bittner Exp $ // // environ.cpp // Implementation of the environment operations, ie. reading // environment file, reading command line parameters etc. // //#define _DEBUG_PARAMS #include #include #include #include #include #include "gzstream.h" #include "common.h" #include "Environment.h" #include "Vector3.h" Environment *environment = NULL; Environment::~Environment() { int i, j; // delete the params structure for (i = 0; i < numParams; i++) { for (j = 0; j < paramRows; j++) if (params[i][j] != NULL) delete[] params[i][j]; if (params[i] != NULL) delete[] params[i]; } if (params != NULL) delete[] params; // delete the options structure if (options != NULL) delete[] options; if (optionalParams != NULL) delete optionalParams; } bool Environment::CheckForSwitch(const int argc, const char *argv[], const char swtch) const { for (int i = 1; i < argc; i++) if ((argv[i][0] == '-') && (argv[i][1] == swtch)) return true; return false; } bool Environment::CheckType(const char *value, const EOptType type) const { char *s, *t, *u; switch (type) { case optInt: { strtol(value, &t, 10); if (value + strlen(value) != t) return false; else return true; } case optFloat: { strtod(value, &t); if (value + strlen(value) != t) return false; else return true; } case optBool: { if (!strcasecmp(value, "true") || !strcasecmp(value, "false") || !strcasecmp(value, "YES") || !strcasecmp(value, "NO") || !strcmp(value, "+") || !strcmp(value, "-") || !strcasecmp(value, "ON") || !strcasecmp(value, "OFF")) return true; return false; } case optVector:{ strtod(value, &s); if (*s == ' ' || *s == '\t') { while (*s == ' ' || *s == '\t') s++; if (*s != ',') s--; } if ((*s != ',' && *s != ' ' && *s != '\t') || value == s) return false; t = s; strtod(s + 1, &u); if (*u == ' ' || *u == '\t') { while (*u == ' ' || *u == '\t') u++; if (*u != ',') u--; } if ((*u != ',' && *s != ' ' && *s != '\t') || t == u) return false; t = u; strtod(u + 1, &s); if (t == s || value + strlen(value) != s) return false; return true; } case optString: { return true; } default: { Debug << "Internal error: Unknown type of option.\n" << flush; exit(1); } } return false; } void Environment::ReadCmdlineParams(const int argc, const char *argv[], const char *optParams) { int i; // Make sure we are called for the first time if (optionalParams != NULL) return; numParams = strlen(optParams) + 1; optionalParams = new char[numParams]; strcpy(optionalParams, optParams); // First, count all non-optional parameters on the command line for (i = 1; i < argc; i++) if (argv[i][0] != '-') paramRows++; // if there is no non-optional parameter add a default one... if (paramRows == 0) paramRows = 1; // allocate and initialize the table for parameters params = new char **[numParams]; for (i = 0; i < numParams; i++) { params[i] = new char *[paramRows]; for (int j = 0; j < paramRows; j++) params[i][j] = NULL; } // Now read all non-optional and optional parameters into the table curRow = -1; for (i = 1; i < argc; i++) { if (argv[i][0] != '-') { // non-optional parameter encountered curRow++; params[0][curRow] = new char[strlen(argv[i]) + 1]; strcpy(params[0][curRow], argv[i]); } else { // option encountered char *t = strchr(optionalParams, argv[i][1]); if (t != NULL) { // this option is optional parameter int index = t - optionalParams + 1; if (curRow < 0) { // it's a global parameter for (int j = 0; j < paramRows; j++) { params[index][j] = new char[strlen(argv[i] + 2) + 1]; strcpy(params[index][j], argv[i] + 2); } } else { // it's a scene parameter if (params[index][curRow] != NULL) { delete[] params[index][curRow]; } params[index][curRow] = new char[strlen(argv[i] + 2) + 1]; strcpy(params[index][curRow], argv[i] + 2); } } } } curRow = 0; #ifdef _DEBUG_PARAMS // write out the parameter table cerr << "Parameter table for " << numParams << " columns and " << paramRows << " rows:\n"; for (int j = 0; j < paramRows; j++) { for (i = 0; i < numParams; i++) { if (params[i][j] != NULL) cerr << params[i][j]; else cerr << "NULL"; cerr << "\t"; } cerr << "\n"; } cerr << "Params done.\n" << flush; #endif // _DEBUG_PARAMS } bool Environment::GetParam(const char name, const int index, char *value) const { int column; if (index >= paramRows || index < 0) return false; if (name == ' ') column = 0; else { char *t = strchr(optionalParams, name); if (t == NULL) return false; column = t - optionalParams + 1; } if (params[column][index] == NULL) return false; // value = new char[strlen(params[column][index]) + 1]; strcpy(value, params[column][index]); return true; } void Environment::RegisterOption(const char *name, const EOptType type, const char *abbrev, const char *defValue) { int i; // make sure this option was not yet registered for (i = 0; i < numOptions; i++) if (!strcmp(name, options[i].name)) { Debug << "Error: Option " << name << " registered twice.\n"; exit(1); } // make sure we have enough room in memory if (numOptions >= maxOptions) { Debug << "Error: Too many options. Try enlarge the maxOptions " << "definition.\n"; exit(1); } // make sure the abbreviation doesn't start with 'D' if (abbrev != NULL && (abbrev[0] == 'D' )) { Debug << "Internal error: reserved switch " << abbrev << " used as an abbreviation.\n"; exit(1); } // new option options[numOptions].type = type; options[numOptions].name = strdup(name); // assign abbreviation, if requested if (abbrev != NULL) { options[numOptions].abbrev = strdup(abbrev); } // assign default value, if requested if (defValue != NULL) { options[numOptions].defaultValue = strdup(defValue); if (!CheckType(defValue, type)) { Debug << "Internal error: Inconsistent type and default value in option " << name << ".\n"; exit(1); } } // new option registered numOptions++; } bool Environment::OptionPresent(const char *name) const { bool found = false; int i; for (i = 0; i < numOptions; i++) if (!strcmp(options[i].name, name)) { found = true; break; } if (!found) { Debug << "Internal error: Option " << name << " not registered.\n" << flush; exit(1); } if (options[i].value != NULL || options[i].defaultValue != NULL) return true; else return false; } int Environment::FindOption(const char *name, const bool isFatal) const { int i; bool found = false; // is this option registered ? for (i = 0; i < numOptions; i++) if (!strcmp(options[i].name, name)) { found = true; break; } if (!found) { // no registration found Debug << "Internal error: Required option " << name << " not registered.\n" << flush; exit(1); } if (options[i].value == NULL && options[i].defaultValue == NULL) // this option was not initialised to some value if (isFatal) { Debug << "Error: Required option " << name << " not found.\n" << flush; exit(1); } else { Debug << "Error: Required option " << name << " not found.\n" << flush; return -1; } return i; } bool Environment::GetIntValue(const char *name, int &value, const bool isFatal) const { int i = FindOption(name, isFatal); if (i<0) return false; if (options[i].value != NULL) { // option was explicitly specified value = strtol(options[i].value, NULL, 10); } else { // option was not read, so use the default value = strtol(options[i].defaultValue, NULL, 10); } return true; } bool Environment::GetDoubleValue(const char *name, double &value, const bool isFatal) const { int i = FindOption(name, isFatal); if (i<0) return false; if (options[i].value != NULL) { // option was explicitly specified value = strtod(options[i].value, NULL); } else { // option was not read, so use the default value = strtod(options[i].defaultValue, NULL); } return true; } bool Environment::GetRealValue(const char *name, Real &value, const bool isFatal) const { int i = FindOption(name, isFatal); if (i<0) return false; if (options[i].value != NULL) { // option was explicitly specified value = strtod(options[i].value, NULL); } else { // option was not read, so use the default value = strtod(options[i].defaultValue, NULL); } return true; } bool Environment::GetFloatValue(const char *name, float &value, const bool isFatal) const { int i = FindOption(name, isFatal); if (i<0) return false; if (options[i].value != NULL) { // option was explicitly specified value = (float)strtod(options[i].value, NULL); } else { // option was not read, so use the default value = (float)strtod(options[i].defaultValue, NULL); } return true; } bool Environment::GetBool(const char *name, const bool isFatal) const { bool ret; if (GetBoolValue(name, ret, isFatal)) return ret; else return false; } bool Environment::ParseBool(const char *name) const { bool value = true; if (!strcasecmp(name, "false") || !strcasecmp(name, "NO") || !strcmp(name, "-") || !strcasecmp(name, "OFF")) value = false; return value; } void Environment::ParseVector(const char *name, Vector3 &v) const { // option was not read, so use the default char *s, *t; v.x = (Real)strtod(name, &s); v.y = (Real)strtod(s + 1, &t); v.z = (Real)strtod(t + 1, NULL); } bool Environment::GetBoolValue(const char *name, bool &value, const bool isFatal) const { int i = FindOption(name, isFatal); if (i<0) return false; if (options[i].value != NULL) value = ParseBool(options[i].value); else value = ParseBool(options[i].defaultValue); return true; } bool Environment::GetVectorValue(const char *name, Vector3 &v, const bool isFatal) const { int i = FindOption(name, isFatal); if (i<0) return false; if (options[i].value != NULL) if (options[i].value != NULL) { ParseVector(options[i].value, v); } else { ParseVector(options[i].defaultValue, v); } return true; } bool Environment::GetStringValue(const char *name, char *value, const bool isFatal) const { int i = FindOption(name, isFatal); if (i<0) return false; if (options[i].value != NULL) { // option was not read, so use the default strcpy(value, options[i].value); } else { // option was explicitly specified strcpy(value, options[i].defaultValue); } return true; } void Environment::SetInt(const char *name, const int value) { int i = FindOption(name); if (i<0) return; if (options[i].type == optInt) { delete options[i].value; options[i].value = new char[16]; sprintf(options[i].value, "%.15d", value); } else { Debug << "Internal error: Trying to set non-integer option " << name << " to integral value.\n" << flush; exit(1); } } void Environment::SetFloat(const char *name, const Real value) { int i = FindOption(name); if (i<0) return; if (options[i].type == optFloat) { delete options[i].value; options[i].value = new char[25]; sprintf(options[i].value, "%.15e", value); } else { Debug << "Internal error: Trying to set non-Real option " << name << " to Real value.\n" << flush; exit(1); } } void Environment::SetBool(const char *name, const bool value) { int i = FindOption(name); if (i<0) return; if (options[i].type == optBool) { delete options[i].value; options[i].value = new char[6]; if (value) sprintf(options[i].value, "true"); else sprintf(options[i].value, "false"); } else { Debug << "Internal error: Trying to set non-bool option " << name << " to boolean value.\n" << flush; exit(1); } } void Environment::SetVector(const char *name, const Vector3 &v) { int i = FindOption(name); if (i<0) return; if (options[i].type == optVector) { delete options[i].value; options[i].value = new char[128]; sprintf(options[i].value, "%.15e,%.15e,%.15e", v.x, v.y, v.z); } else { Debug << "Internal error: Trying to set non-vector option " << name << " to vector value.\n" << flush; exit(1); } } void Environment::SetString(const char *name, const char *value) { int i = FindOption(name); if (i<0) return; if (options[i].type == optString) { delete options[i].value; options[i].value = strdup(value); } else { Debug << "Internal error: Trying to set non-string option " << name << " to string value.\n" << flush; exit(1); } } void Environment::ParseCmdline(const int argc, const char *argv[], const int index) { int curIndex = -1; for (int i = 1; i < argc; i++) { // if this parameter is non-optional, skip it and increment the counter if (argv[i][0] != '-') { curIndex++; continue; } // make sure to skip all non-optional parameters char *t = strchr(optionalParams, argv[i][1]); if (t != NULL) continue; // if we are in the scope of the current parameter, parse it if (curIndex == -1 || curIndex == index) { if (argv[i][1] == 'D') { // it's a full name definition bool found = false; int j; char *t = strchr(argv[i] + 2, '='); if (t == NULL) { Debug << "Error: Missing '=' in option. " << "Syntax is -D=.\n" << flush; exit(1); } for (j = 0; j < numOptions; j++) if (!strncmp(options[j].name, argv[i] + 2, t - argv[i] - 2) && (unsigned)(t - argv[i] - 2) == strlen(options[j].name)) { found = true; break; } if (!found) { Debug << "Warning: Unregistered option " << argv[i] << ".\n" << flush; // exit(1); } if (found) { if (!CheckType(t + 1, options[j].type)) { Debug << "Error: invalid type of value " << t + 1 << " in option " << options[j].name << ".\n"; exit(1); } if (options[j].value != NULL) delete options[j].value; options[j].value = strdup(t + 1); } } else { // it's an abbreviation bool found = false; int j; for (j = 0; j < numOptions; j++) if (options[j].abbrev != NULL && !strncmp(options[j].abbrev, argv[i] + 1, strlen(options[j].abbrev))) { found = true; break; } if (!found) { Debug << "Warning: Unregistered option " << argv[i] << ".\n" << flush; // exit(1); } if (found) { if (!CheckType(argv[i] + 1 + strlen(options[j].abbrev), options[j].type)) { Debug << "Error: invalid type of value " << argv[i] + 1 + strlen(options[j].abbrev) << "in option " << options[j].name << ".\n"; exit(1); } if (options[j].value != NULL) delete options[j].value; options[j].value = strdup(argv[i] + 1 + strlen(options[j].abbrev)); } } } } #ifdef _DEBUG_PARAMS // write out the options table cerr << "Options table for " << numOptions << " options:\n"; for (int j = 0; j < numOptions; j++) { cerr << options[j]; cerr << "\n"; } cerr << "Options done.\n" << flush; #endif // _DEBUG_PARAMS } char * Environment::ParseString(char *buffer, char *string) const { char *s = buffer; char *t = string + strlen(string); // skip leading whitespaces while (*s == ' ' || *s == '\t') s++; if (*s == '\0') return NULL; while ((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') || (*s >= '0' && *s <= '9') || *s == '_') *t++ = *s++; *t = '\0'; // skip trailing whitespaces while (*s == ' ' || *s == '\t') s++; return s; } const char code[] = "JIDHipewhfdhyd74387hHO&{WK:DOKQEIDKJPQ*H#@USX:#FWCQ*EJMQAHPQP(@G#RD"; void Environment::DecodeString(char *buff, int max) { buff[max] = 0; char *p = buff; const char *cp = code; for (; *p; p++) { if (*p != '\n') *p = *p ^ *cp; ++cp; if (*cp == 0) cp = code; } } void Environment::CodeString(char *buff, int max) { buff[max] = 0; char *p = buff; const char *cp = code; for (; *p; p++) { if (*p != '\n') *p = *p ^ *cp; ++cp; if (*cp == 0) cp = code; } } void Environment::SaveCodedFile(char *filenameText, char *filenameCoded) { ifstream envStream(filenameText); // some error had occured if (envStream.fail()) { cerr << "Error: Can't open file " << filenameText << " for reading (err. " << envStream.rdstate() << ").\n"; return; } char buff[256]; envStream.getline(buff, 255); buff[8] = 0; if (strcmp(buff, "CGX_CF10") == 0) return; ofstream cStream(filenameCoded); cStream<<"CGX_CF10"; // main loop for (;;) { // read in one line envStream.getline(buff, 255); if (!envStream) break; CodeString(buff, 255); cStream<= 'a' && *t <= 'z') || (*t >= 'A' && *t <= 'Z') || *t == '+' || *t == '-') t++; if (((!strncasecmp(s, "true", t - s) && t - s == 4) || (!strncasecmp(s, "false", t - s) && t - s == 5) || (!strncasecmp(s, "YES", t -s) && t - s == 3) || (!strncasecmp(s, "NO", t - s) && t - s == 2) || (!strncasecmp(s, "ON", t - s) && t - s == 2) || (!strncasecmp(s, "OFF", t - s) && t - s == 3) || (t - s == 1 && (*s == '+' || *s == '-'))) && (*t == ' ' || *t == '\t' || *t == '\0' || *t == '}')) { if (options[i].value != NULL) delete options[i].value; options[i].value = new char[t - s + 1]; strncpy(options[i].value, s, t - s); options[i].value[t - s] = '\0'; s = t; } else { cerr << "Error: Mismatch in bool variable " << name << " in " << "environment file " << envFilename << " (line " << line << ").\n"; envStream.close(); return false; } break; } case optVector:{ strtod(s, &t); if (*t == ' ' || *t == '\t') { while (*t == ' ' || *t == '\t') t++; if (*t != ',') t--; } if (t == s || (*t != ' ' && *t != '\t' && *t != ',')) { cerr << "Error: Mismatch in vector variable " << name << " in " << "environment file " << envFilename << " (line " << line << ").\n"; envStream.close(); return false; } char *u; strtod(t, &u); t = u; if (*t == ' ' || *t == '\t') { while (*t == ' ' || *t == '\t') t++; if (*t != ',') t--; } if (t == s || (*t != ' ' && *t != '\t' && *t != ',')) { cerr << "Error: Mismatch in vector variable " << name << " in " << "environment file " << envFilename << " (line " << line << ").\n"; envStream.close(); return false; } strtod(t, &u); t = u; if (t == s || (*t != ' ' && *t != '\t' && *t != '\0' && *t != '}')) { cerr << "Error: Mismatch in vector variable " << name << " in " << "environment file " << envFilename << " (line " << line << ").\n"; envStream.close(); return false; } if (options[i].value != NULL) delete options[i].value; options[i].value = new char[t - s + 1]; strncpy(options[i].value, s, t - s); options[i].value[t - s] = '\0'; s = t; break; } case optString: { if (options[i].value != NULL) delete options[i].value; options[i].value = new char[strlen(s) + 1]; strcpy(options[i].value, s); s += strlen(s); break; } default: { Debug << "Internal error: Unknown type of option.\n" << flush; exit(1); } } // prepare the variable name for next pass t = strrchr(name, '.'); if (t == NULL) name[0] = '\0'; else *(t + 1) = '\0'; // get next identifier s = ParseString(s, name); } } envStream.close(); return true; } void Environment::PrintUsage(ostream &s) const { // Print out all environment variable names s << "Registered options:\n"; for (int j = 0; j < numOptions; j++) s << options[j] << "\n"; s << flush; } Environment::Environment() { optionalParams = NULL; paramRows = 0; numParams = 0; params = NULL; maxOptions = 200; // this is maximal nuber of options. numOptions = 0; options = new COption[maxOptions]; if (options == NULL ) { Debug << "Error: Memory allocation failed.\n"; exit(1); } // register all basic options RegisterOption("Limits.threshold", optFloat, NULL, "0.01"); RegisterOption("Limits.small", optFloat, NULL, "1e-6"); RegisterOption("Limits.infinity", optFloat, NULL, "1e6"); RegisterOption("Scene.filename", optString, "-scene_filename=", "atlanta2.x3d"); RegisterOption("Scene.viewcells", optString, "-viewcells_filename=", "atlanta_viewcells_large.x3d"); RegisterOption("Unigraphics.meshGrouping", optInt, "-unigraphics_mesh_grouping=", "0"); RegisterOption("KdTree.Termination.minCost", optInt, "-kd_term_min_cost=", "10"); RegisterOption("KdTree.Termination.maxDepth", optInt, "-kd_term_max_depth=", "20"); RegisterOption("KdTree.Termination.maxCostRatio", optFloat, "-kd_term_max_cost_ratio=", "1.5"); RegisterOption("KdTree.Termination.ct_div_ci", optFloat, "-kd_term_ct_div_ci=", "1.0"); RegisterOption("KdTree.splitMethod", optString, "-kd_split_method=", "spatialMedian"); RegisterOption("KdTree.splitBorder", optFloat, "-kd_split_border=", "0.1"); RegisterOption("KdTree.sahUseFaces", optBool, "-kd_sah_use_faces=", "true"); RegisterOption("MeshKdTree.Termination.minCost", optInt, "-kd_term_min_cost=", "10"); RegisterOption("MeshKdTree.Termination.maxDepth", optInt, "-kd_term_max_depth=", "20"); RegisterOption("MeshKdTree.Termination.maxCostRatio", optFloat, "-kd_term_max_cost_ratio=", "1.5"); RegisterOption("MeshKdTree.Termination.ct_div_ci", optFloat, "-kd_term_ct_div_ci=", "1.0"); RegisterOption("MeshKdTree.splitMethod", optString, "-kd_split_method=", "spatialMedian"); RegisterOption("MeshKdTree.splitBorder", optFloat, "-kd_split_border=", "0.1"); RegisterOption("Sampling.totalSamples", optInt, "-total_samples=", "1000000"); RegisterOption("Sampling.samplesPerPass", optInt, "-total_samples=", "10"); RegisterOption("BspTree.Termination.maxPolygons", optInt, "-bsp_term_max_polygons=", "5"); RegisterOption("BspTree.Termination.maxDepth", optInt, "-bsp_term_max_depth=", "100"); RegisterOption("BspTree.splitPlaneStrategy", optString, "-bsp_split_method=", "leastSplits"); RegisterOption("BspTree.constructionMethod", optString, "-bsp_construction_method=", "viewCells"); RegisterOption("BspTree.maxCandidates", optInt, "-bsp_max_candidates=", "20"); RegisterOption("BspTree.maxViewCells", optInt, "-bsp_max_viewcells=", "9999"); } void Environment::SetStaticOptions() { // get Global option values GetRealValue("Limits.threshold", Limits::Threshold); GetRealValue("Limits.small", Limits::Small); GetRealValue("Limits.infinity", Limits::Infinity); } void Environment::Parse(const int argc, const char *argv[], bool useExePath) { // Read the names of the scene, environment and output files ReadCmdlineParams(argc, argv, ""); char *envFilename = new char[128]; char filename[64]; // Get the environment file name if (!GetParam(' ', 0, filename)) { // user didn't specified environment file explicitly, so strcpy(filename, "default.env"); } if (useExePath) { char *path = GetPath(argv[0]); if (*path != 0) sprintf(envFilename, "%s/%s", path, filename); else strcpy(envFilename, filename); delete path; } else strcpy(envFilename, filename); // Now it's time to read in environment file. if (!ReadEnvFile(envFilename)) { // error - bad input file name specified ? cerr<<"Error parsing environment file "<