// ivpoints.cxx - read Open Inventor "iv" file format for 3-D models // and print out points with known depth in the following format // X Y Z R G B // // This understands only a small subset of the file format, namely // Texture2 { image WIDTH HEIGHT 3 COLOR_IN_HEXADECIMAL ... } // TextureCoordinate2 { point[ X Y, ... X Y ] } // Coordinate3 { point [ X Y Z, ... X Y Z ] } // where all capitals means a numerical or string variable, // and it ignores comments // # EVERYTHING_UP_TO_NEWLINE // and any other tokens it doesn't recognize. // Currently, comments within the Texture2, TextureCoordinate2, and Coordinate3 // commands are not tolerated (this is a bug). // // Written specifically to parse Inventor files written by the // Minolta Vivid 700 laser scanner, // for 15-869 assignment 2: View Transformation // // Paul Heckbert 23 Sept 1999 #include #include // for strcmp #include // for file I/O, includes iostream.h #include // for ios:: #include #include #include #include #include // for color image, from libpicio typedef struct {float r, g, b;} Pixelf_rgb; #define TOKEN_SIZE 20 struct Point { float x, y, z; // 3-D position float s, t; // texture coordinates unsigned char r, g, b; // color }; #define NMAX 40000 // because Vivid scans 200*200 3-D points, max class Object { // a 3-D object with shape and texture public: Pic *pic; // texture Point pt[NMAX]; // points int npt; // number of points double xmin, xmax, ymin, ymax, zmin, zmax; // bounding box for xyz Object(); ~Object(); void get_color_bilinear(float s, float t, unsigned char *r, unsigned char *g, unsigned char *b); // do bilinear interpolation from texture // to read Open Inventor file format void inventor_read(istream &s, const char *streamname); void inventor_read(const char *file); void print(); // print the points private: // to read Open Inventor file format, internals void inventor_read_texture2(istream &s, const char *streamname); void inventor_read_texturecoordinate2(istream &s, const char *streamname); void inventor_read_coordinate3(istream &s, const char *streamname); }; Object::Object() { pic = NULL; npt = 0; xmin = HUGE; xmax = -HUGE; ymin = HUGE; ymax = -HUGE; zmin = HUGE; zmax = -HUGE; } Object::~Object() { if (pic) pic_free(pic); } void Object::get_color_bilinear(float s, float t, unsigned char *r, unsigned char *g, unsigned char *b) { // use bilinear interpolation to extract the color of the // texture pixel at (s,t) from pic float x = s*pic->nx; float y = t*pic->ny; // in case some of the (s,t)'s are outside [0,1] range (some are!) if (x<0) x = 0; if (x>=pic->nx-1) x = pic->nx-1.001; if (y<0) y = 0; if (y>=pic->ny-1) y = pic->ny-1.001; int xi = floor(x); // integer part int yi = floor(y); float xf = x-xi; // fractional part float yf = y-yi; Pixel1_rgb *p00, *p10, *p01, *p11; Pixelf_rgb px0, px1, pxy; p00 = (Pixel1_rgb *)&PIC_PIXEL(pic, xi, yi, 0); p10 = p00+1; // note that we're reading unsigned chars below, but writing floats // linearly interpolate (lerp) between colors 00 and 10 px0.r = p00->r + xf*(p10->r - p00->r); px0.g = p00->g + xf*(p10->g - p00->g); px0.b = p00->b + xf*(p10->b - p00->b); p01 = (Pixel1_rgb *)&PIC_PIXEL(pic, xi, yi+1, 0); p11 = p01+1; // lerp between colors 01 and 11 px1.r = p01->r + xf*(p11->r - p01->r); px1.g = p01->g + xf*(p11->g - p01->g); px1.b = p01->b + xf*(p11->b - p01->b); // lerp between colors x0 and x1 pxy.r = px0.r + yf*(px1.r - px0.r); pxy.g = px0.g + yf*(px1.g - px0.g); pxy.b = px0.b + yf*(px1.b - px0.b); assert(pxy.r>=0 && pxy.r<256); assert(pxy.g>=0 && pxy.g<256); assert(pxy.b>=0 && pxy.b<256); // convert back to unsigned char *r = pxy.r; *g = pxy.g; *b = pxy.b; } // Open Inventor parsing static int next_token_is_not(istream &s, const char *str) { // read a token from stream s, return whether token is not str static char tok[TOKEN_SIZE]; s >> setw(sizeof tok) >> tok; return strcmp(tok, str); } static void expect_token(istream &s, const char *command, const char *str) { // read a token from stream s, and print an error and exit if it's not str char tok[TOKEN_SIZE]; s >> setw(sizeof tok) >> tok; if (strcmp(tok, str)) { cerr << "Error reading '" << command << "' command, got '" << tok << "', not '" << str << "', as expected" << endl; // would be neat to print filename and line number of input file here exit(1); } } static void exit_if_trouble(istream &s, const char *command) { if (s.fail()) { cerr << "Unknown error reading '" << command << "' command" << endl; exit(1); } } // warning: the following routines do a lousy job of checking for errors // (e.g. braces not preceded by space) void Object::inventor_read_texture2(istream &s, const char *streamname) { // Texture2 { image WIDTH HEIGHT 3 COLOR_IN_HEXADECIMAL ... } // read a color image expect_token(s, "Texture2", "{"); expect_token(s, "Texture2", "image"); int nx, ny, x, y, color24; s >> nx; // read width s >> ny; // read height cout << nx << "x" << ny << " texture array" << endl; expect_token(s, "Texture2", "3"); pic = pic_alloc(nx, ny, 3, NULL); // allocate a picture array s.setf(ios::hex); // use hexadecimal notation, not decimal for (y=0; y> color24; // read in hexadecimal 24-bit color exit_if_trouble(s, "Texture2"); // cout << "(" << x << "," << y << ") "; // get the address of pixel (x,y) in pic Pixel1_rgb *p = (Pixel1_rgb *)&PIC_PIXEL(pic, x, y, 0); p->r = color24>>16; // save the bytes p->g = color24>>8; p->b = color24; } s.setf(ios::dec); // back to decimal expect_token(s, "Texture2", "}"); exit_if_trouble(s, "Texture2"); // check that no stream error so far } void Object::inventor_read_texturecoordinate2 (istream &s, const char *streamname) { // TextureCoordinate2 { point[ X Y, ... X Y ] } // read a list of texture coordinates expect_token(s, "TextureCoordinate2", "{"); expect_token(s, "TextureCoordinate2", "point["); // note! we're relying on the quirks of Minolta's VIVID software, // expecting no space between "point" and "[" here! int n; char tok[TOKEN_SIZE]; for (n=0;;) { double x, y; s >> x >> y; // read texture x and y (a.k.a. s and t) assert(n> setw(sizeof tok) >> tok; if (strcmp(tok, ",")) // usually, token is a comma if (!strcmp(tok, "]")) break; // "]" means end of list else { cerr << "Didn't find end of TextureCoordinate2 list in " << streamname << endl; exit(1); } } expect_token(s, "TextureCoordinate2", "}"); exit_if_trouble(s, "TextureCoordinate2"); // check that no error so far cout << "read " << n << " texture coordinates" << endl; npt = n; } void Object::inventor_read_coordinate3(istream &s, const char *streamname) { // Coordinate3 { point [ X Y Z, ... X Y Z ] } // read a list of xyz coordinates expect_token(s, "Coordinate3", "{"); expect_token(s, "Coordinate3", "point"); expect_token(s, "Coordinate3", "["); int n; char tok[TOKEN_SIZE]; for (n=0;;) { double x, y, z; s >> x >> y >> z; // read x, y, and z if (xxmax) xmax = x; if (yymax) ymax = y; if (zzmax) zmax = z; assert(n> setw(sizeof tok) >> tok; if (strcmp(tok, ",")) // usually, token is a comma if (!strcmp(tok, "]")) break; // "]" means end of list else { cerr << "Didn't find end of Coordinate3 list in " << streamname << endl; exit(1); } } expect_token(s, "Coordinate3", "}"); exit_if_trouble(s, "Coordinate3"); // check that no stream error so far cout << "read " << n << " 3-D points" << endl; cout << " bounding box: " << "x [" << xmin << ":" << xmax << "] " << "y [" << ymin << ":" << ymax << "] " << "z [" << zmin << ":" << zmax << "]" << endl; // check that the number of texture points and 3-D points are equal assert(n==npt); } void Object::inventor_read(istream &s, const char *streamname) { // read an Inventor file, returning a texture and a set of points char tok[TOKEN_SIZE]; cout << "reading " << streamname << endl; while (s >> setw(sizeof tok) >> tok) { // cout << "(" << tok << ")" << endl; if (!strcmp(tok, "#")) { // gobble comment s.ignore(1000, '\n'); } else if (!strcmp(tok, "Texture2")) inventor_read_texture2(s, streamname); else if (!strcmp(tok, "TextureCoordinate2")) inventor_read_texturecoordinate2(s, streamname); else if (!strcmp(tok, "Coordinate3")) inventor_read_coordinate3(s, streamname); } cout << "done with " << streamname << endl; } void Object::inventor_read(const char *file) { ifstream s(file, ios::in); if (!s) { cerr << "inventor_read: can't read " << file << endl; exit(1); } inventor_read(s, file); s.close(); } void Object::print() { int i; printf("\n%d points\n# X Y Z R G B\n", npt); for (i=0; i