src/graphic/sprite.cpp

Go to the documentation of this file.
00001 /******************************************************************************
00002  *  Wormux is a convivial mass murder game.
00003  *  Copyright (C) 2001-2004 Lawrence Azzoug.
00004  *
00005  *  This program is free software; you can redistribute it and/or modify
00006  *  it under the terms of the GNU General Public License as published by
00007  *  the Free Software Foundation; either version 2 of the License, or
00008  *  (at your option) any later version.
00009  *
00010  *  This program is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  *  GNU General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU General Public License
00016  *  along with this program; if not, write to the Free Software
00017  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
00018  ******************************************************************************
00019  * Sprite:     Simple sprite management
00020  ******************************************************************************
00021  * 2005/09/21: Jean-Christophe Duberga (jcduberga@gmx.de)
00022  *             Initial version
00023  *****************************************************************************/
00024 
00025 #include "sprite.h"
00026 #include <SDL.h>
00027 #include <SDL_rotozoom.h>
00028 #include <iostream>
00029 #include "surface.h"
00030 #include "../game/game.h"
00031 #include "../include/app.h"
00032 #include "../map/camera.h"
00033 #include "../map/map.h"
00034 #include "../tool/rectangle.h"
00035 #include "../tool/debug.h"
00036 
00037 #define BUGGY_SDLGFX
00038 
00039 Sprite::Sprite() :
00040   cache(*this),
00041   animation(*this)
00042 {
00043   Constructor();
00044 }
00045 
00046 Sprite::Sprite( Surface surface ) :
00047   cache(*this),
00048   animation(*this)
00049 {
00050    Constructor();
00051    frame_width_pix = surface.GetWidth();
00052    frame_height_pix = surface.GetHeight();
00053    frames.push_back( SpriteFrame(surface));
00054 }
00055 
00056 Sprite::Sprite(const Sprite &other) :
00057   cache(*this),
00058   animation(other.animation,*this)
00059 {
00060   frame_width_pix = other.frame_width_pix;
00061   frame_height_pix = other.frame_height_pix;
00062   scale_x = other.scale_x;
00063   scale_y = other.scale_y;
00064   alpha = other.alpha;
00065   rotation_rad = other.rotation_rad;
00066   current_frame = other.current_frame;
00067   rot_hotspot = other.rot_hotspot;
00068   show = other.show;
00069 
00070   for(unsigned int f=0;f<other.frames.size();f++)
00071     AddFrame(other.frames[f].surface,other.frames[f].delay);
00072 }
00073 
00074 void Sprite::Constructor() {
00075   show = true;
00076   current_frame = 0;
00077   frame_width_pix = frame_height_pix = 0;
00078   alpha = 1.0f;
00079   scale_x = scale_y = 1.0f;
00080   rotation_rad = 0.0f;
00081   SetRotation_HotSpot(center);
00082 }
00083 
00084 void Sprite::Init(Surface& surface, const Point2i &frameSize, int nb_frames_x, int nb_frames_y){
00085    Point2i f;
00086 
00087    this->frame_width_pix = frameSize.x;
00088    this->frame_height_pix = frameSize.y;
00089 
00090    surface.SetAlpha( 0, 0);
00091 
00092    for( f.y = 0; f.y < nb_frames_y; f.y++)
00093      for( f.x = 0; f.x < nb_frames_x; f.x++){
00094        Surface new_surf = Surface(frameSize, SDL_SWSURFACE|SDL_SRCALPHA, true);
00095        Rectanglei sr(f * frameSize, frameSize);
00096 
00097        new_surf.Blit( surface, sr, Point2i(0, 0));
00098        frames.push_back( SpriteFrame(new_surf));
00099      }
00100 }
00101 
00102 void Sprite::AddFrame(const Surface &surf, unsigned int delay){
00103           frames.push_back( SpriteFrame(surf, delay) );
00104 }
00105 
00106 void Sprite::SetSize(unsigned int w, unsigned int h){
00107   assert(frame_width_pix == 0 && frame_height_pix == 0)
00108         frame_width_pix = w;
00109         frame_height_pix = h;
00110 }
00111 
00112 void Sprite::SetSize(const Point2i &size){
00113         SetSize(size.x, size.y);
00114 }
00115 
00116 unsigned int Sprite::GetWidth() const{
00117    return static_cast<uint>(frame_width_pix * (scale_x>0?scale_x:-scale_x));
00118 }
00119 
00120 unsigned int Sprite::GetWidthMax() const{
00121   if(!current_surface.IsNull() )
00122     return current_surface.GetWidth();
00123   else
00124     return GetWidth();
00125 }
00126 
00127 unsigned int Sprite::GetHeight() const{
00128    return static_cast<uint>(frame_height_pix * (scale_y>0?scale_y:-scale_y));
00129 }
00130 
00131 unsigned int Sprite::GetHeightMax() const{
00132   if(!current_surface.IsNull() )
00133     return current_surface.GetHeight();
00134   else
00135     return GetHeight();
00136 }
00137 
00138 Point2i Sprite::GetSize() const{
00139         return Point2i(GetWidth(), GetHeight());
00140 }
00141 
00142 Point2i Sprite::GetSizeMax() const{
00143         return Point2i(GetWidthMax(), GetHeightMax());
00144 }
00145 
00146 unsigned int Sprite::GetFrameCount(){
00147    return frames.size();
00148 }
00149 
00150 void Sprite::SetCurrentFrame( unsigned int frame_no){
00151   assert (frame_no < frames.size());
00152   if (current_frame != frame_no) {
00153     cache.InvalidLastFrame();
00154     MSG_DEBUG("sprite", "Set current frame : %d", frame_no);
00155   }
00156   current_frame = frame_no;
00157 }
00158 
00159 unsigned int Sprite::GetCurrentFrame() const{
00160   assert(current_frame < frames.size());
00161   return current_frame;
00162 }
00163 
00164 SpriteFrame& Sprite::operator[] (unsigned int index){
00165   return frames.at(index);
00166 }
00167 
00168 const SpriteFrame& Sprite::operator[] (unsigned int index) const{
00169   return frames.at(index);
00170 }
00171 
00172 const SpriteFrame& Sprite::GetCurrentFrameObject() const{
00173    return frames[current_frame];
00174 }
00175 
00176 void Sprite::Scale( float scale_x, float scale_y){
00177    if(this->scale_x == scale_x && this->scale_y == scale_y)
00178            return;
00179    this->scale_x = scale_x;
00180    this->scale_y = scale_y;
00181    cache.InvalidLastFrame();
00182 }
00183 
00184 void Sprite::ScaleSize(int width, int height){
00185   Scale(float(width)/float(frame_width_pix),
00186         float(height)/float(frame_height_pix));
00187 }
00188 
00189 void Sprite::ScaleSize(Point2i size){
00190         ScaleSize(size.x, size.y);
00191 }
00192 
00193 void Sprite::GetScaleFactors( float &scale_x, float &scale_y){
00194    scale_x = this->scale_x;
00195    scale_y = this->scale_y;
00196 }
00197 
00198 void Sprite::SetFrameSpeed(unsigned int nv_fs){
00199    for ( unsigned int f = 0 ; f < frames.size() ; f++)
00200      frames[f].delay = nv_fs;
00201 }
00202 
00203 void Sprite::SetAlpha( float alpha){
00204   assert(alpha >= 0.0 && alpha <= 1.0);
00205   if(this->alpha == alpha)
00206     return;
00207   this->alpha = alpha;
00208 }
00209 
00210 float Sprite::GetAlpha(){
00211   return alpha;
00212 }
00213 
00214 void Sprite::SetRotation_rad( double angle_rad){
00215    while(angle_rad > 2*M_PI)
00216      angle_rad -= 2 * M_PI;
00217    while(angle_rad <= -2*M_PI)
00218      angle_rad += 2 * M_PI;
00219 
00220    if(rotation_rad == angle_rad) return;
00221 
00222    rotation_rad = angle_rad;
00223    cache.InvalidLastFrame();
00224 }
00225 
00226 const double &Sprite::GetRotation_rad()
00227 {
00228   assert(rotation_rad > -2*M_PI && rotation_rad <= 2*M_PI);
00229   return rotation_rad;
00230 }
00231 
00232 void Sprite::SetRotation_HotSpot( const Point2i new_hotspot)
00233 {
00234   rot_hotspot = user_defined;
00235   rhs_pos = new_hotspot;
00236 
00237   if( rhs_pos.x * 2 == static_cast<int>(GetWidth()) &&
00238       rhs_pos.y * 2 == static_cast<int>(GetHeight())  )
00239     rot_hotspot = center; // avoid using Calculate_Rotation_Offset, thus avoiding a division by zero
00240 }
00241 
00242 void Sprite::Calculate_Rotation_Offset(Surface& tmp_surface){
00243   const SpriteFrame& frame = GetCurrentFrameObject();
00244   const Surface &surface = frame.surface;
00245   // Calculate offset of the depending on hotspot rotation position :
00246 
00247   int surfaceHeight = surface.GetHeight();
00248   int surfaceWidth = surface.GetWidth();
00249 
00250   //Do as if hotspot is center of picture:
00251   rotation_point.x = surfaceWidth  / 2 - tmp_surface.GetWidth()  / 2;
00252   rotation_point.y = surfaceHeight / 2 - tmp_surface.GetHeight() / 2;
00253 
00254   if(rot_hotspot == center)
00255       return;
00256 
00257   if(rot_hotspot != user_defined)
00258   {
00259     switch(rot_hotspot)
00260     {
00261     case top_left:      rhs_pos = Point2i( 0,              0);               break;
00262     case top_center:    rhs_pos = Point2i( surfaceWidth/2, 0);               break;
00263     case top_right:     rhs_pos = Point2i( surfaceWidth,   0);               break;
00264     case left_center:   rhs_pos = Point2i( 0,              surfaceHeight/2); break;
00265     case center:        rhs_pos = Point2i( surfaceWidth/2, surfaceHeight/2); break;
00266     case right_center:  rhs_pos = Point2i( surfaceWidth,   surfaceHeight/2); break;
00267     case bottom_left:   rhs_pos = Point2i( 0,              surfaceHeight);   break;
00268     case bottom_center: rhs_pos = Point2i( surfaceWidth/2, surfaceHeight);   break;
00269     case bottom_right:  rhs_pos = Point2i( surfaceWidth,   surfaceHeight);   break;
00270     default:
00271       assert(false);
00272     }
00273   }
00274 
00275   Point2i rhs_pos_tmp;
00276   rhs_pos_tmp.x = static_cast<uint>(rhs_pos.x * scale_x);
00277   rhs_pos_tmp.y = static_cast<uint>(rhs_pos.y * scale_y);
00278   surfaceWidth  = static_cast<uint>(surfaceWidth  * scale_x);
00279   surfaceHeight = static_cast<uint>(surfaceHeight * scale_y);
00280 
00281   //Calculate the position of the hotspot after a rotation around the center of the surface:
00282   float rhs_dst; //Distance between center of the sprite and the hotspot
00283   double rhs_angle; //Angle of the hotspot _before_ the rotation
00284 
00285   rhs_dst = sqrt(float((surfaceWidth /2 - rhs_pos_tmp.x)*(surfaceWidth /2 - rhs_pos_tmp.x)
00286                      + (surfaceHeight/2 - rhs_pos_tmp.y)*(surfaceHeight/2 - rhs_pos_tmp.y)));
00287 
00288   if( rhs_dst == 0.0)
00289     rhs_angle = 0.0;
00290   else
00291     rhs_angle = - acos ( float(rhs_pos_tmp.x - surfaceWidth/2) / rhs_dst );
00292 
00293   if(surfaceHeight/2 - rhs_pos.y < 0) rhs_angle = -rhs_angle;
00294 
00295   rhs_angle += rotation_rad;
00296 
00297   Point2i rhs_new_pos =  Point2i(surfaceWidth /2 + static_cast<uint>(cos(rhs_angle) * rhs_dst),
00298                                  surfaceHeight/2 + static_cast<uint>(sin(rhs_angle) * rhs_dst));
00299 
00300   rotation_point.x -= rhs_new_pos.x;
00301   rotation_point.y -= rhs_new_pos.y;
00302   rotation_point.x += rhs_pos_tmp.x;
00303   rotation_point.y += rhs_pos_tmp.y;
00304 }
00305 
00306 void Sprite::Start(){
00307    show = true;
00308    animation.Start();
00309    cache.InvalidLastFrame();
00310 }
00311 
00312 void Sprite::Blit( Surface &dest, uint pos_x, uint pos_y){
00313   RefreshSurface();
00314     Blit(dest, pos_x, pos_y, 0, 0, current_surface.GetWidth(), current_surface.GetHeight());
00315 }
00316 
00317 void Sprite::Blit( Surface &dest, const Point2i &pos){
00318         Blit(dest, pos.GetX(), pos.GetY());
00319 }
00320 
00321 void Sprite::Blit( Surface &dest, const Rectanglei &srcRect, const Point2i &destPos){
00322         Blit(dest, destPos.GetX(), destPos.GetY(), srcRect.GetPositionX(), srcRect.GetPositionY(), srcRect.GetSizeX(), srcRect.GetSizeY() );
00323 }
00324 
00325 void Sprite::Blit( Surface &dest, int pos_x, int pos_y, int src_x, int src_y, uint w, uint h){
00326   if (!show)
00327         return;
00328 
00329   RefreshSurface();
00330 
00331   Rectanglei srcRect (src_x, src_y, w, h);
00332   Rectanglei dstRect (pos_x + rotation_point.x, pos_y + rotation_point.y, w, h);
00333 
00334   if(alpha == 1.0)
00335     dest.Blit(current_surface, srcRect, dstRect.GetPosition());
00336   else
00337   {
00338     Surface surf_alpha;
00339     surf_alpha.NewSurface(srcRect.GetSize(),SDL_SWSURFACE,false);
00340     surf_alpha.Blit(dest,dstRect,Point2i(0,0));
00341     surf_alpha.SetAlpha(SDL_SRCALPHA, (int)(alpha * 255.0));
00342     surf_alpha.Blit(current_surface,srcRect,Point2i(0,0));
00343     dest.Blit(surf_alpha, srcRect, dstRect.GetPosition());
00344   }
00345 
00346   // For the cache mechanism
00347   if( Game::GetInstance()->IsGameLaunched() )
00348     world.ToRedrawOnScreen( dstRect );
00349 }
00350 
00351 void Sprite::Finish(){
00352   animation.Finish();
00353   switch(animation.GetShowOnFinish())
00354   {
00355   case SpriteAnimation::show_first_frame:
00356     current_frame = 0;
00357     break;
00358   case SpriteAnimation::show_blank:
00359     show = false;
00360     break;
00361   default:
00362   case SpriteAnimation::show_last_frame:
00363     current_frame = frames.size()-1;
00364     break;
00365   }
00366   cache.InvalidLastFrame();
00367 }
00368 
00369 void Sprite::Update(){
00370   animation.Update();
00371 }
00372 
00373 void Sprite::Draw(const Point2i &pos){
00374   DrawXY(pos - camera.GetPosition());
00375 }
00376 
00377 void Sprite::DrawXY(const Point2i &pos){
00378   if( !show )
00379     return;
00380 
00381   Blit(AppWormux::GetInstance()->video.window, pos);
00382 }
00383 
00384 void Sprite::Show() { show = true; }
00385 void Sprite::Hide() { show = false; }
00386 bool Sprite::IsFinished() const { return animation.IsFinished(); }
00387 
00388 void Sprite::EnableRotationCache(unsigned int cache_size) {
00389   cache.EnableRotationCache(frames, cache_size);
00390 }
00391 
00392 void Sprite::EnableFlippingCache() {
00393   cache.EnableFlippingCache(frames);
00394 }
00395 
00396 void Sprite::RefreshSurface()
00397 {
00398   current_surface.Free();
00399 
00400   if(!cache.have_rotation_cache && !cache.have_flipping_cache)
00401   {
00402     if(!cache.have_lastframe_cache)
00403       current_surface = frames[current_frame].surface.RotoZoom(-rotation_rad, scale_x, scale_y, SMOOTHING_OFF);
00404     else
00405     {
00406       if(cache.last_frame.IsNull() )
00407       {
00408 #ifdef BUGGY_SDLGFX
00409         if(rotation_rad != 0.0 || (scale_x != 1.0 && scale_y == 1.0))
00410         {
00411           current_surface = frames[current_frame].surface.RotoZoom(-rotation_rad , scale_x, scale_y, SMOOTHING_OFF);
00412           cache.last_frame = current_surface;
00413         }
00414         else
00415         if(scale_x != 1.0 || scale_y != 1.0)
00416         {
00417 #endif
00418           current_surface = frames[current_frame].surface.RotoZoom(-rotation_rad, scale_x, scale_y, SMOOTHING_ON);
00419           cache.last_frame = current_surface;
00420 #ifdef BUGGY_SDLGFX
00421         }
00422         else
00423         {
00424           current_surface = frames[current_frame].surface;
00425           cache.last_frame.Free();
00426         }
00427 #endif
00428       }
00429       else
00430       {
00431         current_surface = cache.last_frame;
00432       }
00433     }
00434   }
00435   else
00436   {
00437     if(cache.have_flipping_cache && !cache.have_rotation_cache)
00438     {
00439       if(rotation_rad != 0.0 || scale_y != 1.0 || (scale_x != 1.0 && scale_x != -1.0))
00440       {
00441         current_surface = frames[current_frame].surface.RotoZoom( rotation_rad, scale_x, scale_y, SMOOTHING_OFF );
00442       }
00443       else
00444       if(scale_x == 1.0)
00445         current_surface = frames[current_frame].surface;
00446       else
00447         current_surface = cache.frames[current_frame].flipped_surface;
00448     }
00449     else
00450     if(!cache.have_flipping_cache && cache.have_rotation_cache)
00451     {
00452       if(scale_x != 1.0 || scale_y != 1.0)
00453         current_surface = frames[current_frame].surface.RotoZoom(rotation_rad, scale_x, scale_y, SMOOTHING_OFF);
00454       else
00455         current_surface = cache.frames[current_frame].GetSurfaceForAngle(rotation_rad);
00456     }
00457     else
00458     {
00459       //cache.have_flipping_cache==true && cache.have_rotation_cache==true
00460       if((scale_x != 1.0 && scale_x != -1.0)  || scale_y != 1.0)
00461         current_surface = frames[current_frame].surface.RotoZoom( rotation_rad, scale_x, scale_y, SMOOTHING_OFF);
00462       else
00463       {
00464         //Scale_y == 1.0
00465         if(scale_x == 1.0)
00466           current_surface = cache.frames[current_frame].GetSurfaceForAngle(rotation_rad);
00467         else
00468           current_surface = cache.frames[current_frame].GetFlippedSurfaceForAngle(rotation_rad);
00469       }
00470     }
00471   }
00472   assert( !current_surface.IsNull() );
00473 
00474   // Calculate offset of the sprite depending on hotspot rotation position :
00475   rotation_point.x=0;
00476   rotation_point.y=0;
00477   if(rot_hotspot != center || rotation_rad!=0.0)
00478     Calculate_Rotation_Offset(current_surface);
00479 }
00480 
00481 Surface Sprite::GetSurface() {
00482   assert ( !current_surface.IsNull() );
00483   return current_surface;
00484 }
00485 

Generated on Mon Jan 1 13:10:56 2007 for Wormux by  doxygen 1.4.7