TerraGen tutorial page 4

From Odwiki

Jump to: navigation, search

Back to the beginning: TerragenSOP

Parse the Terragen Terrain File

These are some code fragments demonstrating various aspects to parsing and reading the Terragen terrain file.

Initialize the file state structure which is used to keep track of the parsing process, as each chunk is read, these flags are set and tested when reading subsequent chunks. Their state is used to determine if the file format rules have been adhered to or not:

file_state.size = false;
file_state.xpts = false;
file_state.ypts = false;
file_state.scal = false;
file_state.crad = false;
file_state.crvm = false;
file_state.altw = false;
file_state.eof =  false;


Create a file input stream, it an error occured, return with error().

// Create a file input stream opening the Terragen terrain file
std::ifstream input_file((const char *)myFileName, ios::in | ios::binary);
            
// If we encountered an error when trying to open the Terragen file, add warning and exit
if(!input_file) {
 addWarning(SOP_MESSAGE, "Can't open file for reading!");
 return error();
 }


Enter a try block and start reading the file. As various "chunks" are read, determine if any rules were broken and store the chunk data.

// Enter a "try" block in case we catch an exception
try 
 {        
 // Read the Terragen header
 input_str.clear();        
 // Read 8 bytes and append to the input string
 for(i=0; i < 8; i++) {
  if(input_file.get(ch)) {
   // Append the character to our string
   input_str += ch;    
  }
  else {
   addWarning(SOP_MESSAGE, "Can't read Terragen Header!");
   input_file.close();
   return error();
  }
 }

 // If the string is not the correct first 8 bytes of the header, add a warning and exit
 if(input_str != "TERRAGEN") {
  addWarning(SOP_MESSAGE, "Not a Terragen file!");
  input_file.close();
  return error();
 }

  // Read the Terragen file type
  input_str.clear();        
  // Read 8 bytes and append to the input string
  for(i=0; i < 8; i++) {
   if(input_file.get(ch)) {
    // Append the character to our string
    input_str += ch;    
    }
   else {
    addWarning(SOP_MESSAGE, "Can't read Terragen file type!");
    input_file.close();
    return error();
   }
  }

  // If the string is not the correct next 8 bytes of the header, add a warning and exit
  if(input_str != "TERRAIN ") {
   addWarning(SOP_MESSAGE, "Not a Terragen terrain file!");
   input_file.close();
   return error();
  }

  bool file_status = true;

  //   while not the end of file, parse the chunks ...
  while(file_status) {
   // Read the next chunk "ID" and process it ...
   switch (read_chunk(input_file)) {

   case CHUNK_ERROR:
     addWarning(SOP_MESSAGE, "Can't read Terragen chunk!");
     input_file.close();
     return error();
     break;

   case SIZE_CHUNK:
     if(file_state.altw) {
       addWarning(SOP_MESSAGE, "SIZE chunk must appear BEFORE ALTW chunk!");
       input_file.close();
       return error();
       }
       process_SIZE_chunk(input_file);
       break;


// <SNIP ... do the same type of processing for all chunk types>


     case ALTW_CHUNK:
       if(!file_state.size) {
        addWarning(SOP_MESSAGE, "ALTW chunk must appear AFTER SIZE chunk!");
        input_file.close();
        return error();
       }
       if(!file_state.xpts && file_state.size) {
         addWarning(SOP_MESSAGE, "ALTW chunk must appear AFTER XPTS chunk!");
         input_file.close();
         return error();
        }
       if(!file_state.ypts && file_state.size) {
        addWarning(SOP_MESSAGE, "ALTW chunk must appear AFTER YPTS chunk!");
        input_file.close();
        return error();
        }
        process_ALTW_chunk(input_file, elev_value);
        file_status = false;
        break;


     default:
      addWarning(SOP_MESSAGE, "Unknown chunk type!");
      input_file.close();
      return error();
      }
     }

 // close the file        
 input_file.close();
 }
 catch(std::ios_base::failure& file_exception ) {
  addWarning(SOP_MESSAGE, "Exception thrown during file access");
  input_file.close(); 
  return error();
 }


Here's the method for reading a Terragen Terrain file chunk:

inline int SOP_Terragen::read_chunk(ifstream &input_file) {

    string input_str;
    char ch;

    if(DEBUG) 
        cout << "read_chunk() ";

    // Read a "chunk"
    input_str.clear();        
    for(int i=0; i < 4; i++) {
        if(input_file.get(ch)) {
            // Append the character to our string
            input_str += ch;    
        }
        else {
            return CHUNK_ERROR;
        }
    }

    // Return the chunk type just read
    if(input_str == "SIZE") return SIZE_CHUNK;
    if(input_str == "XPTS") return XPTS_CHUNK;
    if(input_str == "YPTS") return YPTS_CHUNK;
    if(input_str == "SCAL") return SCAL_CHUNK;
    if(input_str == "CRAD") return CRAD_CHUNK;
    if(input_str == "CRVM") return CRVM_CHUNK;
    if(input_str == "ALTW") return ALTW_CHUNK;

    // return a chunk error
    return CHUNK_ERROR;
}


These are some of the methods to handle the different Terragen Terrain file chunks:

inline int SOP_Terragen::process_SIZE_chunk(ifstream &input_file) {

    char ch;

    // Read the next 2 bytes to get the SIZE value
    input_file.read((char *)&size, 2);

    #ifdef BIG_ENDIAN
    UTswapBytes((int16 *)&size, (int16 *)&size, 0);
    #endif

    // Discard the next 2 bytes
    for(int i =0; i < 2; i++)
        input_file.get(ch);

    file_state.size = true;

    return 0;
}

inline int  SOP_Terragen::process_ALTW_chunk(ifstream &input_file, vector <short int> &elev_value) {

    string            input_str;
    short int        myShort;
    char            ch;

    // Read the next 2 bytes to get the "Height Scale" value
    input_file.read((char *)&heightScale, 2);

    #ifdef BIG_ENDIAN
    UTswapBytes((int16 *)&heightScale, (int16 *)&heightScale, 0);
    #endif

    // Read the next 2 bytes to get the "Base Height" value
    input_file.read((char *)&baseHeight, 2);

    #ifdef BIG_ENDIAN
    UTswapBytes((int16 *)&baseHeight, (int16 *)&baseHeight, 0);
    if(DEBUG) 
        cout << "ReadTerragenFile: Base Height chunk (after conversion) =  " << baseHeight << endl;
    #endif

        // Read all of the elevation data and place into the elev_value vector
        for(long int y = 0; y < ypts; y++) {
            for(long int x = 0; x < xpts; x++) {
            // Read the next 2 bytes to get the next "ALTW" value
            input_file.read((char *)&myShort, 2);
            #ifdef BIG_ENDIAN
            UTswapBytes((int16 *)&myShort, (int16 *)&myShort, 0);
            #endif
            elev_value.push_back(myShort);
            }
        }

    file_state.altw = true;

    // Discard 2 bytes 
    input_file.read((char *)&myShort, 2);

    // Read the EOF  "chunk"
    input_str.clear();        
    for(int i=0; i < 4; i++) {
        if(input_file.get(ch)) {
            // Append the character to our string
            input_str += ch;    
        }
        else {
            addWarning(SOP_MESSAGE, "Can't read Terragen EOF chunk!");
            input_file.close();
            return 1;
        }
    }

    if(input_str != "EOF ") {
        addWarning(SOP_MESSAGE, "Can't read the EOF marker in the Terragen terrain file!");
        input_file.close();
        return 1;
    }

    file_state.eof = true;

    return 0;
}
© 2009 od[force].net | advertise