📄 gribparser.cpp
字号:
//#pragma warning( disable : 4786 )//#include <fstream.h>#include "GRibParser.h"#include "../GClasses/GRayTrace.h"// I use uint for readability.#ifndef uinttypedef unsigned int uint;#endifusing std::vector;using std::map;using std::string;using std::list;// Hopefully you'll never need this.//#define PARSER_DEBUGRibParser::RibParser(): m_pScene(NULL), image_width(256), image_height(256), fov( 35 ), baseDir( "" ), in_object_definition( false ){ // Add the root state block. state.push_back( StateBlock() );}RibParser::~RibParser(){ // Delete the memory that the files were read into, since it's no longer needed. for ( uint i = 0; i < lineBuffs.size(); i++ ) delete[] lineBuffs[ i ]; delete(m_pScene);}/*static*/ GRayTraceScene* RibParser::LoadScene(const char* szFilename){ RibParser parser; if(!parser.createScene(szFilename)) return NULL; return parser.DropScene();}// This is just a convenience function, taking a filename instead of a stream.// See the other overloaded method for actual behaviorbool RibParser::createScene(string filename){ FILE *F = fopen(filename.c_str(), "r"); if (!F) return false; // Set the base name on the parser, so that it knows where to find relative files // TODO: test for bugs { string delims = "\\/"; string blank = "";#ifdef WIN32 char mydelim = '\\'; char otherdelim = '/';#else char mydelim = '/'; char otherdelim = '\\';#endif // First, see if there's any directory delimiters string::size_type last_delim = filename.find_last_of( delims ); if ( last_delim != string::npos ) { baseDir = filename; baseDir.erase( last_delim + 1 ); for ( string::size_type i = 0; i < baseDir.size(); i++ ) if ( baseDir[i] == otherdelim ) baseDir[i] = mydelim; } } return createScene(F);}// This reads the whole file at once, then handles each commandbool RibParser::createScene(FILE* F){ // If we're recursing, then m_pScene has already been allocated if(!m_pScene) m_pScene = new GRayTraceScene(); bool success = true; // Let's be optimistic vector< int > lines, line_nums; int total_length; RibData line; total_length = readLines( F, lines, line_nums ); //printf("Read %d lines, parsing...\n", lines.size()); char* line_buff = lineBuffs.back(); // build RibData and handle each line. for ( uint i = 0; i < lines.size(); i++ ) { buildData( line_buff, lines[ i ], line_nums[ i ], line ); success = handleLine( line ) && success; line.clear(); } //printf("Done parsing scene\n"); return success;}int RibParser::readLines( FILE* F, vector< int >& lines, vector< int >& line_nums ){ char *line_buff; string temp; int length; int length_guess; // Oddly enough, length_guess != length. It filters out '\r' because it's in ascii mode.. int num_file_lines = 0; int seek_ahead; int brackets_match = 0; bool empty_line = true; fseek(F, 0, SEEK_END); length_guess = ftell(F); line_buff = new char[length_guess + 4]; fseek(F, 0, SEEK_SET); length = fread(line_buff, 1, length_guess, F); line_buff[length] = '\0'; fclose(F); if (length == 0) { printf("RIB Error! Could not load file.\n"); delete[] line_buff; return 0; } // store the location of line_buff on the RibParser object, so that it will be deleted properly later lineBuffs.push_back( line_buff ); int prev_pos = 0; for ( int i = 0; i < length; i++ ) { switch( line_buff[ i ] ) { case '\r': line_buff[i] = '\n'; // Then fall through case '\n': { // End of line num_file_lines++; if ( empty_line ) { // Whitespace only line. line_buff[ i ] = ' '; // Concat blank lines as trailing whitespace to previous line prev_pos = i+1; continue; } else { // Line with something useful on it is ending. // To make sure it's really the end of the line, // we need to seek ahead to find the first non-whitespace character. // If it's " or [, then this isn't the end of a line (because the next line doesn't // begin with a Rib command - it's a continuation) // We also have to ignore comments, and blank lines. // We also need to make sure we're not in the middle of a list. // This can be kind of a pain. if ( brackets_match != 0 ) { // We're in the middle of a list - keep it all as one line. line_buff[ i ] = ' '; continue; } else { bool end_of_line = true; for ( seek_ahead = i; seek_ahead < length; seek_ahead++ ) { switch( line_buff[ seek_ahead ] ) { case ' ': case '\t': case '\n': // Keep going through whitespace - this isn't what we're looking for. break; case '#': // Eat the rest of the comment - this isn't what we're looking for either. while ( seek_ahead < length ) { if ( line_buff[ seek_ahead ] == '\n' ) break; seek_ahead++; } break; case '\"': case '[': // Not the end of the line end_of_line = false; seek_ahead = length; // This is a way of breaking out of the for loop break; default: end_of_line = true; seek_ahead = length; // This is a way of breaking out of the for loop break; } } if ( !end_of_line ) { // This isn't the end of the line line_buff[ i ] = ' '; continue; } } // At this point, we're sure that this was the end of a line. // Add this line to the vector line_buff[ i ] = '\0'; lines.push_back( prev_pos ); line_nums.push_back( num_file_lines ); // Reset for next line prev_pos = i+1; empty_line = true; } } break; case ' ': case '\t': { // Whitespace } break; case '#': { // Just blank out the comments until the end of the line. We don't need to parse comments. while ( i < length && line_buff[i+1] != '\n' ) { line_buff[i] = ' '; i++; } line_buff[i] = ' '; } break; case '[': brackets_match++; empty_line = false; break; case ']': brackets_match--; empty_line = false; break; default: empty_line = false; } } // We'll check to see if we had a final line that hasn't been added yet if ( !empty_line ) { lines.push_back( prev_pos ); line_nums.push_back( num_file_lines ); } // Return the total length of the stream read. return length;}void RibParser::buildData( char* big_str, int begin, int line_num, RibData& line ){ char* str = big_str + begin; int length = strlen( str ); int prev_pos = 0; char temp; //int brackets_balance = 0; int i; // Find first non-whitespace for ( i = 0; i < length; i++ ) { if ( str[ i ] != ' ' || str[ i ] != '\t' ) break; }#ifdef PARSER_DEBUG if ( str[ i ] == '\"' || str[ i ] == '[' ) { // This shouldn't happen, unless there's a bug in read_lines, or a problem in the file fprintf(stderr, "First token on line is quote or an open bracket.\nThis is an error in the rib file (or perhaps the parser).\n"); }#endif // Set position to first non-whitespace prev_pos = i; // The main state machine loop. for ( i = prev_pos; i < length; i++ ) { switch( str[ i ] ) { case ' ': case '\t': // This is delimiting whitespace. Make it a string terminator str[ i ] = '\0'; break; case '[': { // It's possible to have an open bracket terminate a token. So, let's make it a string terminator also. str[i] = '\0'; // Make the parent RibData and add it to the line. line.append( RibData( RibData::LEAF_ARRAY ) ); // Actually do the extraction. This is somewhat involved, so I made it a separate function this->extractArray( str, length, i, line.back() ); } break;#ifdef PARSER_DEBUG case ']': fprintf(stderr, "Mismatched close bracket\n"); break; case '#': fprintf(stderr, "All the comments should be removed by now..\n"); break;#endif case '\"': // Find the whole quoted string i++; prev_pos = i; while ( i < length && str[ i ] != '\"' ) i++;#ifdef PARSER_DEBUG if ( !( i < length ) ) fprintf(stderr, "Error: Unterminated string.\n");#endif str[ i ] = '\0'; // Zero out the final quote line.append( RibData( &str[ prev_pos ], true ) ); break; default: // Find the whole token prev_pos = i; while ( i < length ) { i++; char c = str[ i ]; if ( c == ' ' || c == '\t' || c == '[' || c == '\"' ) break; }; // The next character might be significant for the state ( '[' and '"' are significant ). // So we'll have to save it, so it can be processed by the state machine. temp = str[ i ]; str[ i ] = '\0'; line.append( RibData( &str[ prev_pos ], false ) ); str[ i ] = temp; i--; // Back up, so as not to miss a character after the loop increment break; } }}void RibParser::extractArray( char* str, int length, int& i, RibData& parent ){ list< char* > temp_vals; // Just store it as a list temporarily. We'll move it to a vector when we know the size // Let's chew through the array values. for ( i++; i < length; i++ ) { if ( str[i] == ' ' || str[i] == '\t' ) { str[i] = '\0'; continue; } else if ( str[i] == ']' ) { str[i] = '\0'; // The array is done - build the vector and return. parent.reserveCapacity( temp_vals.size() ); vector< const char* >& vals = parent.accessVals(); vals.clear(); vals.resize( temp_vals.size(), (const char*)0xdeafbeef ); int ival = 0; std::list< char* >::iterator lit; for ( lit = temp_vals.begin(); lit != temp_vals.end(); lit++, ival++ ) { vals[ ival ] = *lit; } return; } else if ( str[i] == '\"' ) { char* start_ptr = str + i; // We start at the quote, so we know it's a quoted string. // Although the end quote is overwritten by a null, to make // string manipulation a little bit easier. // Get off the current quote i++; // Find matching quote while ( i < length && str[i] != '\"' ) i++; // At this point, we're either at the end of string ( which means malformed RIB ) // or, str[i] == '\"'#ifdef PARSER_DEBUG if ( ! (i < length) ) fprintf(stderr, "Malformed RIB: Unterminated quote, unterminated array\n");#endif // Add the entry to the parent's vals. str[i] = '\0'; // Overwrite the quote with nul, to make string manipulation easier temp_vals.push_back( start_ptr ); } else { // Some normal token: either a string, double, or int. We'll find out when we are asked for it. // This is inner loop for large scenes, so I'm going to use pointer arithmetic instead of indices char* start_ptr = str + i; char* next_ptr = start_ptr + 1; char* end_ptr = start_ptr + length; // Find end of the token. Can be terminated by whitespace, or close bracket. while ( next_ptr != end_ptr ) { if ( *next_ptr == ' ' || *next_ptr == ']' ) break; // Note that a null is written to these positions, to ease string manipulation // The null is actually written to str in the next round of the outer for loop next_ptr++; } i += next_ptr - start_ptr - 1; temp_vals.push_back( start_ptr ); } } fprintf(stderr, "Error in Rib file: Unterminated array\n");}bool RibParser::handleLine( const RibData& data ){#ifdef PARSER_DEBUG if ( data.size() == 0 ) { fprintf(stderr, "The data passed in should always be an array\n"); return false; }#endif bool success = true; string command; command = data[ 0 ].asString(); // If defining an object, store commands to be instanced later. if ( in_object_definition && command != "ObjectEnd" ) { RibData& object_parent = getObjectDefinition( curr_object ); object_parent.append( data ); return true; } // Block management if ( command == "TransformBegin" ) { pushState( StateBlock::TRANSFORMBLOCK ); } else if ( command == "TransformEnd" ) { popState( StateBlock::TRANSFORMBLOCK ); } else if ( command == "AttributeBegin" ) { pushState( StateBlock::ATTRIBUTEBLOCK ); } else if ( command == "AttributeEnd" ) { popState( StateBlock::ATTRIBUTEBLOCK ); } else if ( command == "WorldBegin" ) { pushState( StateBlock::WORLDBLOCK ); } else if ( command == "WorldEnd" ) { popState( StateBlock::WORLDBLOCK ); } else if ( command == "FrameBegin" ) { pushState( StateBlock::FRAMEBLOCK ); } else if ( command == "FrameEnd" ) { popState( StateBlock::FRAMEBLOCK ); } // These are the handlers for various commands else if ( command == "Transform" ) { success = handleTransform( data ); } else if ( command == "ConcatTransform" ) { success = handleConcatTransform( data ); } else if ( command == "Translate" ) { success = handleTranslate( data ); } else if ( command == "ReadArchive" ) { success = handleReadArchive( data ); } else if ( command == "ObjectBegin" ) { success = handleObjectBegin( data ); } else if ( command == "ObjectEnd" ) { success = handleObjectEnd( data ); } else if ( command == "ObjectInstance" ) { success = handleObjectInstance( data ); } else if ( command == "Format" ) { success = handleFormat( data ); } else if ( command == "PixelSamples" ) { success = handlePixelSamples( data ); } else if ( command == "Imager" ) { success = handleImager( data ); } else if ( command == "Projection" ) { success = handleProjection( data ); } else if ( command == "DepthOfField" ) { success = handleDepthOfField( data ); } else if ( command == "Attribute" ) { success = handleAttribute( data ); } else if ( command == "Color" ) { success = handleColor( data ); } else if ( command == "Surface" ) { success = handleSurface( data ); } else if ( command == "LightSource" ) { success = handleLight( data ); } else if ( command == "Sphere" ) { success = handleSphere( data ); } else if ( command == "NuPatch" ) { success = handleNuPatch( data ); } else if ( command == "PointsGeneralPolygons" ) { success = handlePointsGeneralPolygons( data ); } else if ( command == "AreaLightSource" ) { success = handleAreaLightSource( data ); } else if ( command == "Display" || command == "Clipping" || command == "ReverseOrientation" || command == "Orientation" ) { // Silently ignore these commands. } else { printf("Warning: Unhandled RIB data: %s\n", command.c_str()); //data.debugPrint( cout, 3 ); } return success;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -