/* $Id: spriteset.hpp,v 1.16.4.1 2006/01/20 11:33:53 chfreund Exp $ */
#ifndef SPRITE_SET_CLASS_DEFINED
#define SPRITE_SET_CLASS_DEFINED
/**********************************************************
* needed source files: string.cpp settings.cpp
* sprite.cpp spritesequence.cpp
* spriteset.cpp
**********************************************************/
#include "spritesequence.hpp"
#include "progresslog.hpp"
/**********************************************************/
#define TPLCOL_RGB(r,g,b) (((((Uint32)r)&0xff)<<16) | \
((((Uint32)g)&0xff)<< 8) | \
( ((Uint32)b)&0xff) )
#define TPLCOL_RED(c) ((c >> 16) & 0xff)
#define TPLCOL_GREEN(c) ((c >> 8) & 0xff)
#define TPLCOL_BLUE(c) ((c ) & 0xff)
#define TPLCOL_NOT_USED(c) (c & (0x01 << 24))
#define TPLCOL_AS_IS(c) (c & (0x01 << 25))
#define TPLCOL_DONT_USE TPLCOL_NOT_USED(0xffffffff)
#define TPLCOL_DONT_COL TPLCOL_AS_IS(0xffffffff)
/**********************************************************/
/*! This class represents a vector of various sprite sequences, i.e.
* a vector with entries of type class SpriteSequence. All functions
* concerning graphic is already contained in class Sprite. SpriteSet
* offers a comfortable way to load various sprite sequences as defined
* in a configuration file with special format. This loading mechanism
* is supported by just one public function SpriteSet:load.
*
* Color Templates
* A sprite set can be loaded and treated as a template for different
* colors. Let's assume you load the sprite sets for the avatar in a
* game. If the game has more than one player you might want to use
* one sprite set for all players, but colored with different colors.
* The class Sprite supports coloring of all grayscale pixels in a
* sprite (see Sprite::colorGrayPixels). Thus we could create a
* colored instance from each sprite in the sprite set, which means
* a colore instance from the whole sprite set. To enable this coloring
* you have to specify the flag "templated = true".
* If the set is templated a vector of sprite sets will be created in
* the protected section of the class. Whenever a new colored instance
* is created, it will be placed in this vector. If a colored instance
* is demanded (see SpriteSet::getColoredInstance), that is already
* present, no new one will be created. Each of these instances must
* remember its colors. So the routines setColorID and compareColorID
* should be used internally only.
* There is also the possibility to create a colored instance from more
* than one layer, colored with different colors. Let's think of an
* example: You want to have striped worms as avatars in your game.
* Create images for the worms, create images showing their stripes,
* load them templated. Then you could create worms in their player
* color, wearing stripes in their team color. To specify the number
* of layers in a sprite set, set the setting "nlayers". If nlayers
* == n, the n subsequent sequences in the sprite set are assumed to
* build the n layers for one sequence, respectively. The tolerance
* value for coloring (see Sprite::colorGrayPixels) can be set by
* "graytol".
* If the sprite is composed out of several layers you may want the
* resulting sprite to be smoothed. This can be achieved by setting
* "smooth = ". The smoothing works as simple weighted
* averaging on a five point stencil. The four boundary pixels
* always have weight 1, the weight of the center pixel is specified
* as parameter of setting "smooth". The colors RGB are calculated
* separately. The pixels are changed in lexicographic order, and
* the new values are written immediately. Therefore this is a
* Gauss-Seidel like iteration. This smoothing works in templates
* sets only. The alpha channel or the keycolor map is not changed.
*
*
* SYNTAX of the configuration file
*
* The configuration file consists of two main parts. The map part,
* and the sequence part. All settings in the configuration file must
* be assigned expicitly by a "=". Different setting strings must
* be separated by a comma. Every character following after a "#" in
* a line is ignored as comment.
*
*
* The map part specifies an arbitrary number of sequences, just by
* giving each of them a name. These names are used only temporarily
* inside this configuration file, but must be unique inside the
* configuration file; they are set by setting the variable "sequences".
*
* e.g.:
* \code
* sequences = walk_left, walk_right,
* jump_left, jump_right
* \endcode
*
*
* There might also be a list of subdirectories, containing further
* configuration files of the same type, defining further sprite sets.
* These subdirectories are specified by setting the variable "subdirs".
* The "subdirs" setting only specifies the path, where the additional
* configuration file can be found. This path is interpreted relatively
* to the one, where this configuration file can be found.
*
* e.g.:
* \code
* subdirs = dig, fly, crawl
* \endcode
*
*
* Similar to the function "SpriteSet::load" we also need the name of
* the additional configuration file. These filenames are specified by
* setting the variable "subconf". The names of the configuration files
* in the subdirectories must have the same ordering as the sub-
* directories (setting "subdirs") they are contained in.
*
* e.g.:
* \code
* subconf = dig.conf fly.conf crawl.conf
* \endcode
*
*
* Optional: You can specify an ID string (here called object ID) for
* a sprite set. This ID must be unique for the whole sprite set. The
* loading routine will print out an error, if you specify a different
* ID string in one of the subdirectories (see keyword subdir). If you
* have specified the ID string in a directory, this ID is valid for
* all subdirectories. You can only specifiy the same string again in
* the sets loaded from subdirectories using "subdirs".
*
* e.g.:
* \code
* objectID = AVATAR
* \endcode
*
*
* Optional: Definition as color template (see above) using "templated",
* "nlayers" (mandatory for "templated"), "graytol" (optional).
*
* e.g.:
* \code
* templated = yes
* nlayers = 2
* graytol = 10
* \endcode
*
*
* For the definition of subdirectories and their configuration files
* there also exist some modified modes. If they are defined as described
* below, then the number of subdirectory paths and subdirectory con-
* figuration files must be identical. You can also specify some
* subdirectories, but only one name for a configuration file. In
* this case this file name is used for each of the subdirectories.
* Vice versa it is also possible to define one subdirectory and
* several configuration files. Then it is assumed, that all of these
* configuration files are contained in the one subdirectory.
*
* As optional setting for a sprite set a object ID string can be
* defined by setting the variable "objectID". This ID must be valid
* for the whole sprite set. That means that sprite sets included from
* subdirectories are only allowed to set an identical object ID or
* none at all.
*
* This definition part of the "map" of the sprite set must be con-
* tained inside a configuration section with name "SET_MAP". That means
* that it is introduced by the line ">>section_enter<< SET_MAP" and
* ended by the line ">>section_leave<< SET_MAP". It is important that
* there is no space in front of these directives.
*
* The sequence part must be embedded in a section named like one of
* the strings, assigned to the setting "sequences" in the map section
* "SET_MAP". Inside this section the configuration of a sprite sequence
* must be placed. For a description of the configuration of a sprite
* sequence see "spritesequence.hpp".
*
*
* example for a set configuration file:
* (the indentation is for your eyes only, but not necessary)
*
* \code
* >>section_enter<< SET_MAP
* templated = yes
* nlayers = 1
* graytol = 10
* smooth = 6
* sequences = walk_left, walk_right, \ ## walking
* jump_left, jump_right ## jumping
* subdirs = dig, special/fly
* subconf = set.conf
* >>end<< # not necessary, but accelerates the reading of the
* # configuration file, since it causes an immediate
* # termination of reading and this is no problem, because
* # there is no second section "SET_MAP"
* >>section_leave<< SET_MAP
*
* >>section_enter<< walk_left
* frames = walk_left%d.jpg
* indices = 0, 5
* hotspot = 5, 11
* >>end<<
* >>section_leave<< walk_left
*
* >>section_enter<< walk_right
* frames = walk_right%d.jpg
* indices = 0, 5
* hotspot = 5, 11
* >>end<<
* >>section_leave<< walk_right
*
* >>section_enter<< jump_left
* frames = jump_left%02d.jpg
* indices = 0, 12
* colorkey = 0, 255, 0
* >>end<<
* >>section_leave<< jump_left
*
* >>section_enter<< jump_right
* frames = jump_right%02d.jpg
* indices = 0, 12
* >>end<<
* >>section_leave<< jump_right
* \endcode
*/
template
class SpriteSet : public PointerVector< SpriteSequence >,
public SpriteSetSettingDefs
{
public:
SpriteSet();
~SpriteSet();
//! access the object ID
const String& getObjectID() const { return m_ObjectID; }
//! returns true, if set is templated
bool isTemplated() const { return NULL != m_Instances; }
//! returns number of present colored instances
int getNumInstances() const { return m_Instances
? m_Instances->getSize()
: 0; }
//! returns the number of layers
int getNumLayers() const { return m_nLayers; }
//! returns a pointer to the colored instance [i]
const SpriteSet* getColorInstance( const int i ) {
return (const SpriteSet*)(*m_Instances)[i]; }
//! sets the color ID (only used, if this SpriteSet is an instance)
void setColorID( const Uint32 *const colors, const int ncolors );
//! compare the color ID (only used, if this SpriteSet is an instance)
bool compareColorID( const Uint32 *const colors ) const;
//! loading set as defined in configuration file
/*! Loads the spriteset as specified in the configuration file
* with the full path "path/configfile". The separate passing
* of the path, where the configuration file can be found, and
* the name of the configuration file is crucial, since all
* paths given in this configuration file a interpreted
* relatively to the passed "path".
* \param path full path where the configuration file
* can be found
* \param name of the configuration file
* \return number of loaded sprite sequences
*/
int load( const char* const path,
const char* const configfile,
ProgressLog* const progresslog = NULL );
// resets the class to the state after construction
void reset();
//! returns the index of an instance with specified color
/*! If this sprite set is a color template (see documentation
* of this class), this function supports the creation of the
* colored instances and the access to them. This function
* can only be called successfully, if the sprite set has been
* created as a color template (e.g. check SpriteSet::isTemplated()).
* The first parameter is an Uint32 pointer, that must point
* to an array, containing the colors needed for an
* instantiation. That means that the size of this array must
* be equal to the number of layers (this can be retrieved
* from SpriteSet::getNumLayers()). The colors must have the
* format, that is defined by the macros TPLCOL_* (TemPLate
* COLors) in spriteset.hpp (explanation below). The colors
* will be applied in their ordering in the array to the
* corresponding layer (colors[i] is used for sequence[(n *
* #layers) + i]).
* The function will inspect the already present instances
* and return the index of the instance, thats colors match
* coincide with all passed colors. If no matching instance can
* be found, the function tries to create a new instance, but
* only, if the flag "build" is set to true.
*
* color format:
* RGB colors, bits[0-7] = blue, bits[8-15] = green,
* bits[16-23] = red, bit[24] = layer is not used,
* bit[26] = use layer as is (do not color)
*
* color macros:
* - TPLCOL_RGB(red,green,blue) :
* creates a color Uint32 from the color components red,
* green, blue
* - TPLCOL_RED(color) :
* extracts the red color component
* - TPLCOL_GREEN(color) :
* extracts the green color component
* - TPLCOL_BLUE(color) :
* extracts the blue color component
* - TPLCOL_NOT_USED(color) :
* extracts bit[25]. If this bit is set, the layer belonging
* to this color is not used. To set this bit in a color C,
* write C |= TPLCOL_DONT_USE.
* - TPLCOL_AS_IS(color) :
* extracts bit[26]. If this bit is set, the layer belonging
* to this color is used, but not colored. To set this bit
* in a color C, write C |= TPLCOL_DONT_COL.
* - TPLCOL_DONT_USE :
* simply the constant, in which only the bit for omitted
* use is set
* - TPLCOL_DONT_COL :
* simply the constant, in which only the bit for usage
* without coloring is set
*/
int getColoredInstance( const Uint32 *const colors,
const bool build = true,
const real smoothingweight = -1.0 );
//! deletes the colored instance at index "index"
void deleteColoredInstance( const int index );
protected:
//! reads the basic settings of the sprite set
bool readMapSettings( const char* const configfile,
SettingDataBase& settings );
//! some special checks of settings and images, if the set is templated
bool checkTemplate() const;
const char* SettingID( const int i ) const;
// get string of sequence [i]
void getSequence( SettingDataBase& settings,
const int i,
String& string ) const;
//
void getSubdir( SettingDataBase& settings,
const int i,
String& string ) const;
//
void getSubdirConf( SettingDataBase& settings,
const int i,
String& string ) const;
String m_ObjectID; //! object ID, if defined
// data for templated sprite sequences
PointerVector< SpriteSet > *m_Instances; //! eventual template
Uint32 *m_Colors; //! colors, IF this is a colored instance
int m_nColors, //! number of colors
m_nLayers, //! number of layers, if the set is templated
m_grayTolerance;
real m_SmoothCenterWeight;
};
/**********************************************************/
#endif // SPRITE_SET_CLASS_DEFINED