/* $Id: spriteset.cpp,v 1.17.4.2 2006/03/26 09:54:29 chfreund Exp $ */ #include "spriteset.hpp" #include "global.hpp" /**********************************************************/ #ifndef BLACK_ #ifdef SUPPRESS_COLORED_OUTPUT #define RED(s) s #define BLACK_(s) s #define CYAN(s) s #else #define RED(s) "\033[31m" << s << "\033[0m" #define BLACK_(s) "\033[0;1m" << s << "\033[0m" #define CYAN(s) "\033[36m" << s << "\033[0m" #endif // SUPPRESS_COLORING #endif // BLACK_ /**********************************************************/ template SpriteSet::SpriteSet() : m_Instances( NULL ), m_Colors( NULL ), m_nColors( 0 ), m_nLayers( 0 ), m_grayTolerance( 5 ), m_SmoothCenterWeight( -1.0 ) { } /**********************************************************/ template SpriteSet::~SpriteSet() { reset(); } /**********************************************************/ template void SpriteSet::setColorID( const Uint32 *const colors, const int ncolors ) { if( ncolors != m_nColors ) { delete [] m_Colors; m_Colors = NULL; m_nColors = 0; } if( ncolors > 0 ) { if( NULL == m_Colors ) { if( NULL == (m_Colors = NEW Uint32 [ncolors]) ) { cerr << PVEC_FN_ERR("SpriteSet::setColorID") << "could not store " << ncolors << " colors\n"; return; } } memcpy( m_Colors, colors, ncolors*sizeof(Uint32) ); m_nColors = ncolors; } } /**********************************************************/ template bool SpriteSet::compareColorID( const Uint32 *const colors ) const { if( m_Colors == NULL ) return false; for( int i = 0; i < m_nColors; i++ ) { if( colors[i] != m_Colors[i] ) return false; } return true; } /**********************************************************/ template int SpriteSet::load( const char* const path, const char* const configfile, ProgressLog* const progresslog ) { // initialize progress object Progress* progress = NULL; if( progresslog != NULL ) { if( progresslog->getNumLevels() <= 0 ) { progresslog->create( 30, 1 ); } progress = &progresslog->getProgressObject( 0 ); progress->setProgress( 0.0 ); progress->setTitleUnvalid(); } const char fn[] = "SpriteSet::load"; // reset the class reset(); // build full path String Path( path ); const int pathlength = Path.getLength() + 1; Path += "/"; Path += configfile; // read settings SettingDataBase settings; if( false == readMapSettings(Path, settings) ) { cerr << PVEC_CALLED_FROM("SpriteSet::load"); return -1; } // log object ID to the progress log if( progress ) progress->setTitle( m_ObjectID ); // get number of sequences int nSequences = -1; Setting *setting = NULL; if( NULL != (setting = settings.getSetting(SettingID(DEF_SEQUENCES))) ) { if( strcmp(setting->getString(0), "NONE") ) { nSequences = setting->getNumParameters(); } } else { setting = settings.getSetting(SettingID(DEF_SEQUENCE_INDICES)); nSequences = setting->getInt(1) - setting->getInt(0) + 1; } // set new size of the array if( false == this->resize(nSequences) ) { cerr << PVEC_CALLED_FROM( fn ); return -1; } // load all sequences SpriteSequence *newSequence = NULL; for( int seq = 0; seq < nSequences; seq++ ) { // create the string with the restriction belonging to sequence [i] String seqRestriction; getSequence( settings, seq, seqRestriction ); // build a new sprite sequence object if( NULL == (newSequence = NEW SpriteSequence) ) { cerr << PVEC_FN_ERR(fn) << "could not create new sprite sequence object\n"; return 0; } // load the sprite sequence from section in config file if( 0 >= newSequence->load(path, configfile, seqRestriction) ) { cerr << PVEC_CALLED_FROM(fn) << " -> loaded a sequence of size 0, defined as " << BLACK_(seqRestriction) << "\n in " << BLACK_(Path) << endl; return 0; } // add the new sequence to the set newSequence = this->replace( seq, newSequence ); #ifdef _DEBUG_ if( NULL != newSequence ) { cerr << PVEC_FN_ERR(fn) << "replaced a non-NULL-pointer by the new sequence\n" " -> this is a serious bug\n"; } #endif // update progress log, if present if( progress ) *progress = (seq+1) / static_cast(nSequences); } // load sets from subdirectories if( settings.getSetting(SettingID(DEF_SUBDIRS)) && settings.getSetting(SettingID(DEF_SUBDIR_CONF)) ) { // array of sprite sets to buffer the sequences // until we know their number int nSets = settings.getNumParameters( SettingID(DEF_SUBDIRS) ); if( nSets == 1 ) { nSets = settings.getNumParameters( SettingID(DEF_SUBDIR_CONF) ); } PointerVector< SpriteSet > setbuffer( nSets ); // load the sets from the subdirectories and keep // them temporarily in the array "setbuffer" String subdir, subdirconf; SpriteSet *newSet = NULL; int nSubdirSequences = 0; for( int i = 0; i < nSets; i++ ) { // build the full subdirectory path and // get the name of the configuration file getSubdir( settings, i, subdir ); getSubdirConf( settings, i, subdirconf ); Path.cut( pathlength ); Path += subdir; // build a new sprite set object if( NULL == (newSet = NEW SpriteSet) ) { cerr << PVEC_FN_ERR(fn) << "could not create new sprite set object\n"; return 0; } // load sprite set in subdirectory if( 0 == newSet->load(Path, subdirconf, progresslog) ) { cerr << PVEC_FN_ERR(fn) << "loaded sprite set of size 0\n"; } else LOG(1) { cout << "loading set from subdirectory: " << BLACK_(subdir) << ", " << BLACK_(subdirconf) << endl; } // check the ID of the loaded set const String& subID = newSet->getObjectID(); if( (!m_ObjectID.getLength() && subID.getLength() ) || ( m_ObjectID.getLength() && m_ObjectID != subID) ) { cerr << PVEC_FN_ERR(fn) << BLACK_(path) << "\n loaded sprite set with object ID \"" << BLACK_(newSet->getObjectID()) << "\"\n " "in a subdirectory of set with object ID \"" << BLACK_(m_ObjectID) << "\"\n"; } // increment the number of sequences, added in subdirectories nSubdirSequences += newSet->getSize(); newSet = setbuffer.replace( i, newSet ); #ifdef _DEBUG_ if( NULL != newSequence ) { cerr << PVEC_FN_ERR(fn) << "replaced a non-NULL-pointer by the new set\n" " -> this is a serious bug\n"; } #endif } int nextPosition = this->getSize(); // enlarge the set if( false == this->resize(this->getSize() + nSubdirSequences) ) { cerr << PVEC_CALLED_FROM(fn); return this->getSize(); } // add the sequences from the subdirectories for( int i = 0; i < nSets; i++ ) { for( int j = 0; j < setbuffer[i]->getSize(); j++ ) { this->replace( nextPosition++, setbuffer[i]->unhook(j) ); } } } // set number of layers if( settings.getSetting(SettingID(DEF_TEMPLATED)) && settings.getSetting(SettingID(DEF_TEMPLATED))->getBool() ) { if( settings.getSetting(SettingID(DEF_NLAYERS)) ) { m_nLayers = settings.getSetting(SettingID(DEF_NLAYERS))->getInt(); if( m_nLayers <= 0 ) m_nLayers = 1; } if( NULL == (m_Instances = new PointerVector()) ) { cerr << PVEC_FN_ERR( fn ) << "could not create object for colored instances\n"; return -1; } // check templates, if the set was loaded as template if( false == checkTemplate() ) return -1; } return this->getSize(); } /**********************************************************/ template void SpriteSet::reset() { PointerVector< SpriteSequence >::reset(); m_ObjectID.reset(); delete m_Instances; m_Instances = NULL; delete [] m_Colors; m_Colors = NULL; m_nColors = 0; m_nLayers = 0; m_grayTolerance = 5; m_SmoothCenterWeight = -1.0; } /********************************************************** * **********************************************************/ template int SpriteSet::getColoredInstance( const Uint32 *const colors, const bool build, const real smoothingweight ) { const char fn[] = "SpriteSet::getColoredInstance"; if( !isTemplated() ) return -1; // check the already present instances for( int i = 0; i < m_Instances->getSize(); i++ ) { if( (*m_Instances)[i] && (*m_Instances)[i]->compareColorID(colors) ) { return i; } } // if we could not find a matching instance and the // creation of a matching one is not wanted, leave if( !build ) return -1; ////////////////////////////// // build new colored instance SpriteSet *newSet = NEW SpriteSet; ASSERT( newSet, "could not create new SpriteSet in %s\n", fn ); // resize the set to the number of needed seqences ASSERT( newSet->resize(this->getSize()/m_nLayers), "could not resize SpriteSet in %s\n", fn ); //////////////////////// // create each sequence SpriteSequence *newSequence = NULL; for( int seq = 0; seq < newSet->getSize(); seq++ ) { // create new sequence object newSequence = NEW SpriteSequence( this->m_Data[seq * m_nLayers]->getSize() ); ASSERT( newSequence, "could not create new SpriteSequence nr. " "%d in %s\n", seq, fn ); // run trough the frames for( int fra = 0; fra < newSequence->getSize(); fra++ ) { // create a new Sprite object T *newFrame = NEW T( *(*this->m_Data[seq * m_nLayers])[fra] ), layer; if( !TPLCOL_AS_IS(colors[0]) ) { // color the gray pixels of the first layer if( !newFrame->colorGrayPixels( TPLCOL_RED (colors[0]), TPLCOL_GREEN(colors[0]), TPLCOL_BLUE (colors[0]), m_grayTolerance ) ) { cerr << PVEC_CALLED_FROM( fn ); } } // run through the ADDITIONAL layers (start at index 1) for( int lay = 1; lay < m_nLayers; lay++ ) { // if the layer is used if( !TPLCOL_NOT_USED(colors[lay]) ) { // create a copy of the layer frame layer = *(*this->m_Data[seq*m_nLayers + lay])[fra]; // if not omitted, color its gray pixels if( !TPLCOL_AS_IS(colors[lay]) ) { if( !layer.colorGrayPixels( TPLCOL_RED (colors[lay]), TPLCOL_GREEN(colors[lay]), TPLCOL_BLUE (colors[lay]), m_grayTolerance ) ) { cerr << PVEC_CALLED_FROM( fn ); } } // draw the colored layer frame on the base layer layer.draw( *newFrame, 0, 0, NULL, true ); } } // smooth the new frame, if specified if( m_SmoothCenterWeight > 0 ) { newFrame->smooth( m_SmoothCenterWeight ); } // insert new frame newSequence->set( fra, newFrame ); } // set new sequence object in sprite set newSet->set( seq, newSequence ); } ////////////////////////// // new sprite set created // set the color ID in the new instance newSet->setColorID( colors, m_nLayers ); // get index of the next free position in instances int position = m_Instances->getNextFree(); // if the next free position in the vector is not already // present, resize the vector first and place the new instance if( position >= m_Instances->getSize() ) m_Instances->resize( position+1 ); m_Instances->set( position, newSet ); return position; } /********************************************************** * deletes colored instance at passed index **********************************************************/ template void SpriteSet::deleteColoredInstance( const int index ) { // unhooks the specified set and deletes it WITHOUT // filling the created hole (false) // (call of PointerVector< SpriteSequence >::remove) m_Instances->remove( index, false ); } /********************************************************** * Reads the basic settings for this sprite set, i.e. the settings * inside the section "SET_PLAN" **********************************************************/ template bool SpriteSet::readMapSettings( const char* const configfile, SettingDataBase& settings ) { const char fn[] = "SpriteSet::readSettings"; // restrict reading to the section "SET_MAP" if( false == settings.restrictToSection("SET_MAP") ) { cerr << PVEC_CALLED_FROM(fn); return false; } // read settings bool pickySuccess = true; if( false == settings.readSettings(m_SettingDef, (char*)configfile, true, &pickySuccess) || false == pickySuccess || false == settings.finalCheck(m_SettingDef) ) { cerr << PVEC_CALLED_FROM(fn) << " -> sprite set from \"" << BLACK_(configfile) << "\" not loaded\n"; return false; } // store ID string, if specified if( settings.getSetting(SettingID(DEF_OBJECT_ID)) ) { m_ObjectID = settings.getSetting(SettingID(DEF_OBJECT_ID))->getString(); } // get grayscale tolerance, if specified if( settings.getSetting(SettingID(DEF_GRAYTOLERANCE)) ) { m_grayTolerance = settings.getSetting(SettingID(DEF_GRAYTOLERANCE))->getInt(); } // smoothing? if( settings.getSetting(SettingID(DEF_SMOOTH)) ) { m_SmoothCenterWeight = static_cast( settings.getSetting(SettingID(DEF_SMOOTH))->getReal() ); } #ifdef _DEBUG_ // check the validity of the sequence definition Setting *setting_seq = settings.getSetting(SettingID(DEF_SEQUENCES)), *setting_for = settings.getSetting(SettingID(DEF_SEQUENCE_FORMAT)), *setting_ind = settings.getSetting(SettingID(DEF_SEQUENCE_FORMAT)); if( (NULL == setting_seq && (NULL == setting_for || NULL == setting_ind )) || (NULL != setting_seq && (NULL != setting_for || NULL != setting_ind )) ) { cerr << PVEC_FN_ERR( fn ) << "non-conform configuration file \"" << BLACK_(configfile) << "\"\n the list of sequences must be specified by " << "EITHER\n " << BLACK_(SettingID(DEF_SEQUENCES)) << " OR\n " << BLACK_(SettingID(DEF_SEQUENCE_FORMAT)) << " AND " << SettingID(DEF_SEQUENCE_INDICES) << endl; return false; } // check index definition, if present if( NULL != setting_ind && setting_ind->getInt(0) > setting_ind->getInt(1) ) { cerr << PVEC_FN_ERR( fn ) << "non-conform configuration file \"" << BLACK_(configfile) << "\"\n index range of sequences is negative\n"; return false; } // check correctness of sub directory specification Setting *setting_sd = settings.getSetting(SettingID(DEF_SUBDIRS)), *setting_sc = settings.getSetting(SettingID(DEF_SUBDIR_CONF)); if(( NULL != setting_sd && (NULL == setting_sc || (setting_sc->getNumParameters() != 1 || setting_sc->getNumParameters() != setting_sd->getNumParameters() ))) || ( NULL == setting_sd && NULL != setting_sc )) { cerr << PVEC_FN_ERR( fn ) << "non-conform configuration file \"" << BLACK_(configfile) << "\"\n subdirectory specification must contain\n" << " (1) subdirectories : " << BLACK_(SettingID(DEF_SUBDIRS)) << "\n (2) configuration files in these subdirectories : " << SettingID(DEF_SUBDIR_CONF) << "\n -> the number of " " subdirectories and configuration files must be (1,n), " "(n,1), or (n,n)\n"; return false; } // template stuff if( settings.getSetting(SettingID(DEF_TEMPLATED)) && settings.getSetting(SettingID(DEF_TEMPLATED))->getBool() ) { if( !settings.getSetting(SettingID(DEF_NLAYERS)) ) { cerr << PVEC_FN_ERR( fn ) << "set declared as templated, but number of layers not " "specified -> assuming 1\n"; } else if( settings.getSetting(SettingID(DEF_NLAYERS))->getInt() < 1 ) { cerr << PVEC_FN_ERR( fn ) << "number of layers must be a positive number -> " "assuming 1\n"; } } else if( settings.getSetting(SettingID(DEF_NLAYERS)) ) { cerr << PVEC_FN_ERR( fn ) << "specified a number of layers (" << settings.getSetting(SettingID(DEF_NLAYERS))->getInt() << "), although the sprite set is not declared templated" " -> ignored\n"; } // grayscale tolerance if( settings.getSetting(SettingID(DEF_GRAYTOLERANCE)) ) { // not templated if( !settings.getSetting(SettingID(DEF_TEMPLATED)) || !settings.getSetting(SettingID(DEF_TEMPLATED))->getBool() ) { cerr << PVEC_FN_ERR( fn ) << "specified \"" < ignored\n"; } // check range of grayscale tolerance if( m_grayTolerance < 0 || m_grayTolerance > 255 ) { cerr << PVEC_FN_ERR( fn ) << SettingID(DEF_GRAYTOLERANCE) << " = " << m_grayTolerance << ", which is out of the usual range [0,255]\n"; } } // smoothing if( m_SmoothCenterWeight > 0 && (!settings.getSetting(SettingID(DEF_TEMPLATED)) || !settings.getSetting(SettingID(DEF_TEMPLATED))->getBool() ) ) { cerr << PVEC_FN_ERR( fn ) << "activated smoothing with center weight " << m_SmoothCenterWeight << " only has effect in color templated sprite sets -> ignored\n"; } #endif return true; } /********************************************************** * **********************************************************/ template bool SpriteSet::checkTemplate() const { if( m_Instances == NULL ) return true; const char fn[] = "SpriteSet::checkTemplate"; // check the correctness of layer numbers and number of sequences if( this->getSize() % m_nLayers ) { cerr << PVEC_FN_ERR( fn ) << "set " << BLACK_(m_ObjectID) << " has loaded " << this->getSize() << " sequences, which\n" << " is not a multible of the number of layers (" << m_nLayers << ")\n"; return false; } // check the length of the sequences for( int seq = 0; seq < this->getSize() / m_nLayers; seq++ ) { const int length = this->m_Data[seq * m_nLayers]->getSize(); for( int lay = 0; lay < m_nLayers; lay++ ) { if( length != this->m_Data[seq*m_nLayers + lay]->getSize() ) { cerr << PVEC_FN_ERR( fn ) << "length of layer sequences differ in set " << BLACK_(m_ObjectID) << endl; #ifdef _DEBUG_ cerr << " sequence : " << seq << ", layer [0]:" << length << ", [" << lay << "]:" << this->m_Data[seq*m_nLayers + lay]->getSize() << endl; #endif return false; } } } return true; } /********************************************************** * returns string of setting [i] * For "i" use enums in class declaration ( < NUM_SETTING_DEFS) only **********************************************************/ template const char* SpriteSet::SettingID( const int i ) const { return (i >= 0 && i < NUM_SETTING_DEFS) ? m_SettingDef[i].m_IDString : NULL; } /********************************************************** * writes string of sequence nr. i into "string" **********************************************************/ template void SpriteSet::getSequence( SettingDataBase& settings, const int i, String& string ) const { if( NULL != settings.getSetting(SettingID(DEF_SEQUENCES)) ) { string = settings.getSetting(SettingID(DEF_SEQUENCES))->getString(i); } else { string.format( SettingID(DEF_SEQUENCE_FORMAT), settings.getSetting(SettingID(DEF_SEQUENCE_INDICES)) ->getInt(0) + i ); } } /********************************************************** * writes string of subdirectory nr. i into "string" **********************************************************/ template void SpriteSet::getSubdir( SettingDataBase& settings, const int i, String& string ) const { string = settings.getNumParameters(SettingID(DEF_SUBDIRS)) == 1 ? settings.getSetting(SettingID(DEF_SUBDIRS))->getString(0) : settings.getSetting(SettingID(DEF_SUBDIRS))->getString(i); } /********************************************************** * writes string of subdirectory configuration file nr. i into "string" **********************************************************/ template void SpriteSet::getSubdirConf( SettingDataBase& settings, const int i, String& string ) const { string = settings.getNumParameters(SettingID(DEF_SUBDIR_CONF)) == 1 ? settings.getSetting(SettingID(DEF_SUBDIR_CONF))->getString(0) : settings.getSetting(SettingID(DEF_SUBDIR_CONF))->getString(i); } /**********************************************************/