TerraGen tutorial page 4
From Odwiki
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;
}



