--Loading an OFF file-- The following implementation of the Serialize method is one way to handle loading an OFF file, with the amount of detail we need. Color data is ignored, the default OFF type (3D, no vertex normals, colors, or texture coordinates) is mandatory, and the OFF keyword must start the file. To utilize the stream types, you should add: #include #include to your Document header (.h) file. This function does not store any of the data it reads into the class. You will still need to decide on your own data structures, and then modify this code to copy the data read here into your data structures. You will almost definitely want to do some additional processing within this function to analyze the incoming file and store more information than just is what contained in the file. This code does the bare minimum of reading the file only. The only other file-related function you may want to override is the DeleteContents function (Added through the Class Wizard). This function is called just before a File Open, and should delete whatever data you currently have in your Doc. (It is also called before the destructor. To observe when it is called, add the function, place a breakpoint in it with F9, and run the program with F5 (Debug Mode). Play with File->Open and quitting, and see where the breakpoint is hit). If you add this function, then you should remove the loaded mesh from your Doc within that function, and initialize the Doc to empty. DeleteContents will be called before this function when loading, so you can possibly simplify this function with the assumption that your Doc is empty. This is a nice touch, but is not required. void Doc::Serialize(CArchive& ar) { if (ar.IsStoring()) { // If the user hit Save, this block is executed // instead of the one below. } else { // Declare temporary variables to read data into. // If the read goes well, we'll copy these into // our class variables, overwriting what used to // be there. If it doesn't, we won't have messed up // our previous data structures. int tempNumPoints = 0; // Number of x,y,z coordinate triples int tempNumFaces = 0; // Number of polygon sets int tempNumEdges = 0; // Unused, except for reading. double** tempPoints = NULL; // An array of x,y,z coordinates. int** tempFaces = NULL; // An array of arrays of point // pointers. Each entry in this // is an array of integers. Each // integer in that array is the // index of the x, y, and z // coordinates in the corresponding // arrays. int* tempFaceSizes = NULL; // An array of polygon point counts. // Each of the arrays in the tempFaces // array may be of different lengths. // This array corresponds to that // array, and gives their lengths. int i; // Generic loop variable. bool goodLoad = true; // Set to false if the file appears // not to be a valid OFF file. char tempBuf[128]; // A buffer for reading strings // from the file. // Create an input file stream for the file the CArchive // is connected to. This allows use of the overloaded // extraction operator for conversion from string // data to doubles and ints. ar.Flush(); const CFile* fp = ar.GetFile(); ifstream ifs = ifstream(fp->GetFilePath(), ios::in|ios::nocreate, filebuf::sh_read); // Grab the first string. If it's "OFF", we think this // is an OFF file and continue. Otherwise we give up. ifs >> tempBuf; if (strcmp(tempBuf, "OFF") != 0) { goodLoad = false; } // Read the sizes for our two arrays, and the third // int on the line. If the important two are zero // sized, this is a messed up OFF file. Otherwise, // we setup our temporary arrays. if (goodLoad) { ifs >> tempNumPoints >> tempNumFaces >> tempNumEdges; if (tempNumPoints < 1 || tempNumFaces < 1) { // If either of these were negative, we make // sure that both are set to zero. This is // important for later deleting our temporary // storage. goodLoad = false; tempNumPoints = 0; tempNumFaces = 0; } else { tempPoints = new double*[tempNumPoints]; tempFaces = new int*[tempNumFaces]; tempFaceSizes = new int[tempNumFaces]; } } if (goodLoad) { // Load all of the points. for (i = 0; i < tempNumPoints; i++) { tempPoints[i] = new double[3]; ifs >> tempPoints[i][0] >> tempPoints[i][1] >> tempPoints[i][2]; } // Load all of the faces. for (i = 0; i < tempNumFaces; i++) { // This tells us how many points make up // this face. ifs >> tempFaceSizes[i]; // So we declare a new array of that size tempFaces[i] = new int[tempFaceSizes[i]]; // And load its elements with the vertex indices. for (int j = 0; j < tempFaceSizes[i]; j++) { ifs >> tempFaces[i][j]; } // Clear out any face color data by reading up to // the newline. 128 is probably considerably more // space than necessary, but better safe than // sorry. ifs.getline(tempBuf, 128); } } // Here is where we copy the data from the temp // structures into our permanent structures. We // probably will do some more processing on the // data at the same time. This code you must fill // in on your own. if (goodLoad) { } // Now that we're done, we have to make sure we // free our dynamic memory. for (i = 0; i < tempNumPoints; i++) { delete []tempPoints[i]; } delete []tempPoints; for (i = 0; i < tempNumFaces; i++) { delete tempFaces[i]; } delete []tempFaces; delete []tempFaceSizes; // Clean up our ifstream. The MFC framework will // take care of the CArchive. ifs.close(); // If something went wrong, now we'll let the framework know. // Note that we've cleaned up after ourselves first. Code // after this would not be executed if the load was bad, // because the exception will move up to the calling // function. (CDocument::OnFileOpen()). This error handling // is still not very good as exceptions in the code above // may have skipped our clean-up code. A real program would have // to do better. if (!goodLoad) { AfxThrowArchiveException(CArchiveException::badSchema, NULL); } } } Ryan Holmes 2/23/02