Merge
authorWolfgang Steffens <WolfgangSteff@gmail.com>
Tue, 10 Jul 2012 11:09:38 +0200
changeset 7374 514138949c76
parent 7304 8b3575750cd2 (diff)
parent 7372 fcc002658832 (current diff)
child 7377 1aceade403ba
Merge
hedgewars/uGearsRender.pas
hedgewars/uScript.pas
hedgewars/uTypes.pas
hedgewars/uVariables.pas
hedgewars/uWorld.pas
--- a/hedgewars/hwengine.pas	Mon Jul 09 23:28:02 2012 -0400
+++ b/hedgewars/hwengine.pas	Tue Jul 10 11:09:38 2012 +0200
@@ -31,7 +31,7 @@
 
 uses SDLh, uMisc, uConsole, uGame, uConsts, uLand, uAmmos, uVisualGears, uGears, uStore, uWorld, uInputHandler, uSound,
      uScript, uTeams, uStats, uIO, uLocale, uChat, uAI, uAIMisc, uRandom, uLandTexture, uCollisions,
-     SysUtils, uTypes, uVariables, uCommands, uUtils, uCaptions, uDebug, uCommandHandlers, uLandPainted
+     SysUtils, uTypes, uVariables, uCommands, uUtils, uCaptions, uDebug, uCommandHandlers, uLandPainted, uTextures
      {$IFDEF SDL13}, uTouch{$ENDIF}{$IFDEF ANDROID}, GLUnit{$ENDIF};
 
 {$IFDEF HWLIBRARY}
@@ -412,6 +412,7 @@
         //uLandGraphics does not need initialization
         //uLandObjects does not need initialization
         //uLandTemplates does not need initialization
+        uTextures.initModule;
         uLandTexture.initModule;
         //uLocale does not need initialization
         uRandom.initModule;
@@ -461,6 +462,7 @@
     uIO.freeModule;
     uLand.freeModule;
     uLandPainted.freeModule;
+    uTextures.freeModule;
 
     uCommandHandlers.freeModule;
     uCommands.freeModule;
--- a/hedgewars/options.inc	Mon Jul 09 23:28:02 2012 -0400
+++ b/hedgewars/options.inc	Tue Jul 10 11:09:38 2012 +0200
@@ -30,6 +30,8 @@
 {$DEFINE USE_LUA_SCRIPT}
 
 
+{$DEFINE GL2}
+
 {$IFDEF ANDROID}
     {$DEFINE MOBILE}
     {$DEFINE USE_SDLTHREADS}
@@ -53,6 +55,10 @@
 {$ENDIF}
 
 
+{$IFDEF GL2}
+    {$DEFINE S3D_DISABLED}
+{$ENDIF}
+
 {$IFDEF WIN32}
     {$DEFINE USE_CONTEXT_RESTORE}
 {$ENDIF}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uAtlas.pas	Tue Jul 10 11:09:38 2012 +0200
@@ -0,0 +1,764 @@
+{$INCLUDE "options.inc"}
+{$IF GLunit = GL}{$DEFINE GLunit:=GL,GLext}{$ENDIF}
+
+unit uAtlas;
+
+interface
+
+uses SDLh, uTypes;
+
+procedure initModule;
+
+function Surface2Tex_(surf: PSDL_Surface; enableClamp: boolean): PTexture;
+procedure FreeTexture_(sprite: PTexture);
+procedure DebugAtlas;
+procedure DumpInfo(tex: PTexture);
+
+implementation
+
+uses GLunit, uBinPacker, uDebug, png, sysutils, uTextures;
+
+const
+    MaxAtlases = 4;    // Maximum number of atlases (textures) to allocate
+    MaxTexSize = 1024; // Maximum atlas size in pixels
+    MinTexSize = 128;  // Minimum atlas size in pixels
+    CompressionThreshold = 0.4; // Try to compact (half the size of) an atlas, when occupancy is less than this
+
+type
+    AtlasInfo = record
+        PackerInfo: Atlas;     // Rectangle packer context
+        TextureInfo: TAtlas;   // OpenGL texture information
+        Allocated: boolean;    // indicates if this atlas is in use
+        DumpID: Integer;
+    end;
+
+var
+    Info: array[0..MaxAtlases-1] of AtlasInfo;
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Debug routines
+
+procedure DumpInfo(tex: PTexture);
+var
+    frame: Integer;
+    i, atlasID: Integer;
+    aw, ah: Integer;
+begin
+    if tex = nil then
+        exit;
+
+    frame:= 0;
+    writeln(stdout, 'Texture: ' + IntToHex(Integer(tex), 8));
+
+    while tex <> nil do
+    begin
+        atlasID:= -1;
+        for i:= 0 to Pred(MaxAtlases) do
+            if tex^.atlas = @Info[i].TextureInfo then
+                atlasID:=i;
+
+        aw:= tex^.atlas^.w;
+        ah:= tex^.atlas^.h;   
+ 
+        writeln(stdout, 'Frame   : ' + IntToStr(frame));
+        writeln(stdout, 'Size    : ' + IntToStr(tex^.w) + 'x' + IntToStr(tex^.h));
+        writeln(stdout, 'Atlas   : ' + IntToStr(atlasID));
+        writeln(stdout, 'Location: ' + IntToStr(tex^.x) + 'x' + IntToStr(tex^.y));
+        writeln(stdout, 'TB      : ' + '(' + FloatToStrF(tex^.tb[0].X, ffFixed, 15, 4) + ',' + FloatToStrF(tex^.tb[0].Y, ffFixed, 15, 4) + ') '
+                                     + '(' + FloatToStrF(tex^.tb[1].X, ffFixed, 15, 4) + ',' + FloatToStrF(tex^.tb[1].Y, ffFixed, 15, 4) + ') '
+                                     + '(' + FloatToStrF(tex^.tb[2].X, ffFixed, 15, 4) + ',' + FloatToStrF(tex^.tb[2].Y, ffFixed, 15, 4) + ') '
+                                     + '(' + FloatToStrF(tex^.tb[3].X, ffFixed, 15, 4) + ',' + FloatToStrF(tex^.tb[3].Y, ffFixed, 15, 4) + ')');
+
+        writeln(stdout, 'TB.ABS  : ' + '(' + FloatToStrF(tex^.tb[0].X * aw, ffFixed, 15, 4) + ',' + FloatToStrF(tex^.tb[0].Y * ah, ffFixed, 15, 4) + ') '
+                                     + '(' + FloatToStrF(tex^.tb[1].X * aw, ffFixed, 15, 4) + ',' + FloatToStrF(tex^.tb[1].Y * ah, ffFixed, 15, 4) + ') '
+                                     + '(' + FloatToStrF(tex^.tb[2].X * aw, ffFixed, 15, 4) + ',' + FloatToStrF(tex^.tb[2].Y * ah, ffFixed, 15, 4) + ') '
+                                     + '(' + FloatToStrF(tex^.tb[3].X * aw, ffFixed, 15, 4) + ',' + FloatToStrF(tex^.tb[3].Y * ah, ffFixed, 15, 4) + ')');
+
+        inc(frame);
+        tex:= tex^.nextFrame;
+    end;
+    halt(0);
+end;
+
+procedure AssertCount(tex: PTexture; count: Integer);
+var
+    i, j: Integer;
+    found: Integer;
+begin
+    found:= 0;
+    for i:= 0 to pred(MaxAtlases) do
+    begin
+        if not Info[i].Allocated then
+            continue;
+        for j:=0 to pred(Info[i].PackerInfo.usedRectangles.count) do
+        begin
+            if Info[i].PackerInfo.usedRectangles.data[j].UserData = tex then
+                inc(found);
+        end;
+    end;
+    if found <> count then
+    begin
+        writeln('AssertCount(', IntToHex(Integer(tex), 8), ') failed, found ', found, ' times');
+
+        for i:= 0 to pred(MaxAtlases) do
+        begin
+            if not Info[i].Allocated then
+                continue;
+            for j:=0 to pred(Info[i].PackerInfo.usedRectangles.count) do
+            begin
+                if Info[i].PackerInfo.usedRectangles.data[j].UserData = tex then
+                    writeln(' found in atlas ', i, ' at slot ', j);
+            end;
+        end;
+        halt(-2);
+    end;
+end;
+
+var
+    DumpFile: File of byte;
+
+const
+    PNG_COLOR_TYPE_RGBA = 6;
+    PNG_COLOR_TYPE_RGB = 2;
+    PNG_INTERLACE_NONE = 0;
+    PNG_COMPRESSION_TYPE_DEFAULT = 0;
+    PNG_FILTER_TYPE_DEFAULT = 0;
+    
+
+
+procedure writefunc(png: png_structp; buffer: png_bytep; size: QWord); cdecl;
+var
+    p: Pbyte;
+    i: Integer;
+begin
+  //TStream(png_get_io_ptr(png)).Write(buffer^, size);
+    BlockWrite(DumpFile, buffer^, size);
+{    p:= PByte(buffer^);
+    for i:=0 to pred(size) do
+    begin
+        Write(DumpFile, p^);
+        inc(p);
+    end;}
+end;
+
+function IntToStrPad(i: Integer): string;
+var
+  s: string;
+begin
+   s:= IntToStr(i);
+   if (i < 10) then s:='0' + s;
+   if (i < 100) then s:='0' + s;
+   if (i < 1000) then s:='0' + s;
+
+   IntToStrPad:=s;
+end;
+
+// GL1 ATLAS DEBUG ONLY CODE!
+procedure DebugAtlas;
+{$IFDEF DEBUG_ATLAS}
+var
+    vp: array[0..3] of GLint;
+    prog: GLint;
+    i: Integer;
+    x, y: Integer;
+const
+    SZ = 512;
+begin
+    x:= 0;
+    y:= 0;
+    for i:= 0 to pred(MaxAtlases) do
+    begin
+        if not Info[i].allocated then
+            continue;
+        glGetIntegerv(GL_VIEWPORT, @vp);
+{$IFDEF GL2}
+        glGetIntegerv(GL_CURRENT_PROGRAM, @prog);
+        glUseProgram(0);
+{$ENDIF}
+        glPushMatrix;
+        glLoadIdentity;
+        glMatrixMode(GL_PROJECTION);
+        glPushMatrix;
+        glLoadIdentity;
+        glOrtho(0, vp[2], vp[3], 0, -1, 1);
+
+        glDisable(GL_CULL_FACE);
+
+        glBindTexture(GL_TEXTURE_2D, Info[i].TextureInfo.id);
+        glBegin(GL_QUADS);
+        glTexCoord2f(0.0, 0.0);
+        glVertex2i(x * SZ, y * SZ);
+        glTexCoord2f(1.0, 0.0);
+        glVertex2i((x + 1) * SZ, y * SZ);
+        glTexCoord2f(1.0, 1.0);
+        glVertex2i((x + 1) * SZ, (y + 1) * SZ);
+        glTexCoord2f(0.0, 1.0);
+        glVertex2i(x * SZ, (y + 1) * SZ);
+        glEnd();
+
+        glPopMatrix;
+        glMatrixMode(GL_MODELVIEW);
+        glPopMatrix;
+
+        inc(x);
+        if (x = 2) then
+        begin
+            x:=0;
+            inc(y);
+        end;
+     
+{$IFDEF GL2}
+        glUseProgram(prog);
+{$ENDIF}
+    end;
+end;
+{$ELSE}
+begin;
+end;
+{$ENDIF}
+
+procedure DumpAtlas(var dinfo: AtlasInfo);
+var
+    png: png_structp;
+    png_info: png_infop;
+    w, h, sz: Integer;
+    filename: string;
+    rows: array of png_bytep;
+    size: Integer;
+    i, j: Integer;
+    idx: Integer;
+    mem, p, pp: PByte;
+begin
+    idx:= -1;
+    for i:= 0 to Pred(MaxAtlases) do
+        if @dinfo = @Info[i] then
+            idx:=i;
+
+    filename:= '/home/wolfgangst/hedgewars/dump/atlas_' + IntToStr(idx) + '_' + IntToStrPad(dinfo.DumpID) + '.png';
+    Assign(DumpFile, filename);
+    inc(dinfo.DumpID);
+    Rewrite(DumpFile);
+
+    w:= dinfo.TextureInfo.w;
+    h:= dinfo.TextureInfo.h;
+    size:= w * h * 4;
+    SetLength(rows, h);
+    GetMem(mem, size);
+
+    glBindTexture(GL_TEXTURE_2D, dinfo.TextureInfo.id);
+
+    glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, mem);
+
+    p:= mem;
+    for i:= 0 to pred(h) do
+    begin
+        rows[i]:= p;
+        pp:= p;
+        inc(pp, 3);
+        {for j:= 0 to pred(w) do
+        begin
+            pp^:=255;
+            inc(pp, 4);
+        end;}
+        inc(p, w * 4);
+    end;
+
+    png := png_create_write_struct(PNG_LIBPNG_VER_STRING, nil, nil, nil);
+    png_info := png_create_info_struct(png);
+
+    png_set_write_fn(png, nil, @writefunc, nil);
+    png_set_IHDR(png, png_info, w, h, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+    png_write_info(png, png_info);
+    png_write_image(png, @rows[0]);
+    png_write_end(png, png_info);
+    png_destroy_write_struct(@png, @png_info);
+
+    FreeMem(mem);
+    
+    SetLength(rows, 0);
+    Close(DumpFile);
+
+    //if (DumpID >= 30) then
+    //    halt(0);
+end;
+
+////////////////////////////////////////////////////////////////////////////////
+// Upload routines
+
+function createTexture(width, height: Integer): TAtlas;
+var
+  nullTex: Pointer;
+begin
+    createTexture.w:= width;
+    createTexture.h:= height;
+    createTexture.priority:= 0;
+    glGenTextures(1, @createTexture.id);
+    glBindTexture(GL_TEXTURE_2D, createTexture.id);
+
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+    //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil);
+    
+    GetMem(NullTex, width * height * 4);
+    FillChar(NullTex^, width * height * 4, 0);
+    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NullTex);
+    FreeMem(NullTex);
+
+    glBindTexture(GL_TEXTURE_2D, 0);
+end;
+
+function Min(x, y: Single): Single;
+begin
+  if x < y then
+    Min:=x
+  else Min:=y;
+end;
+
+function Max(x, y: Single): Single;
+begin
+  if x > y then
+    Max:=x
+  else Max:=y;
+end;
+
+
+procedure HSVToRGB(const H, S, V: Single; out R, G, B: Single); 
+const 
+    SectionSize = 60/360; 
+var 
+    Section: Single; 
+    SectionIndex: Integer; 
+    f: single; 
+    p, q, t: Single; 
+begin
+    if H < 0 then 
+    begin 
+        R:= V; 
+        G:= R; 
+        B:= R; 
+    end 
+    else 
+    begin 
+        Section:= H/SectionSize; 
+        SectionIndex:= Trunc(Section); 
+        f:= Section - SectionIndex; 
+        p:= V * ( 1 - S ); 
+        q:= V * ( 1 - S * f ); 
+        t:= V * ( 1 - S * ( 1 - f ) ); 
+        case SectionIndex of 
+            0: 
+            begin 
+                R:= V; 
+                G:= t; 
+                B:= p; 
+            end; 
+            1: 
+            begin 
+                R:= q; 
+                G:= V; 
+                B:= p; 
+            end; 
+            2: 
+            begin 
+                R:= p; 
+                G:= V; 
+                B:= t; 
+            end; 
+            3: 
+            begin 
+                R:= p; 
+                G:= q; 
+                B:= V; 
+            end; 
+            4: 
+            begin 
+                R:= t; 
+                G:= p; 
+                B:= V; 
+            end; 
+            else 
+                R:= V; 
+                G:= p; 
+                B:= q; 
+        end; 
+    end; 
+end; 
+
+procedure DebugColorize(surf: PSDL_Surface);
+var
+    sz: Integer;
+    p: PByte;
+    i: Integer;
+    r, g, b, a, inva: Integer;
+    randr, randg, randb: Single;
+    randh: Single;
+begin
+    sz:= surf^.w * surf^.h;
+    p:= surf^.pixels;
+    //randr:=Random;
+    //randg:=Random;
+    //randb:=1 - min(randr, randg);
+    randh:=Random;
+    HSVToRGB(randh, 1.0, 1.0, randr, randg, randb);
+    for i:=0 to pred(sz) do
+    begin
+        a:= p[3];
+        inva:= 255 - a;
+
+        r:=Trunc(inva*randr + p[0]*a/255);
+        g:=Trunc(inva*randg + p[1]*a/255);
+        b:=Trunc(inva*randb + p[2]*a/255);
+        if r > 255 then r:= 255;
+        if g > 255 then g:= 255;
+        if b > 255 then b:= 255;
+
+        p[0]:=r;
+        p[1]:=g;
+        p[2]:=b;
+        p[3]:=255;
+        inc(p, 4);
+    end;
+end;
+
+procedure Upload(var info: AtlasInfo; sprite: Rectangle; surf: PSDL_Surface);
+var
+    sp: PTexture;
+    i, j, stride: Integer;
+    scanline: PByte;
+    r: TSDL_Rect;
+begin
+    //writeln('Uploading sprite to ', sprite.x, ',', sprite.y, ',', sprite.width, ',', sprite.height);
+    sp:= PTexture(sprite.UserData);
+    sp^.x:= sprite.x;
+    sp^.y:= sprite.y;
+    sp^.isRotated:= sp^.w <> sprite.width;
+    sp^.atlas:= @info.TextureInfo;
+
+    if SDL_MustLock(surf) then
+        SDLTry(SDL_LockSurface(surf) >= 0, true);
+
+    //if GrayScale then
+    //    Surface2GrayScale(surf);
+    //DebugColorize(surf);
+
+    glBindTexture(GL_TEXTURE_2D, info.TextureInfo.id);
+    if (sp^.isRotated) then
+    begin
+        scanline:= surf^.pixels;
+        for i:= 0 to pred(sprite.width) do
+        begin
+            glTexSubImage2D(GL_TEXTURE_2D, 0, sprite.x + i, sprite.y, 1, sprite.height, GL_RGBA, GL_UNSIGNED_BYTE, scanline);
+            inc(scanline, sprite.height * 4);
+        end;
+    end
+    else
+        glTexSubImage2D(GL_TEXTURE_2D, 0, sprite.x, sprite.y, sprite.width, sprite.height, GL_RGBA, GL_UNSIGNED_BYTE, surf^.pixels);
+    glBindTexture(GL_TEXTURE_2D, 0);
+
+    if SDL_MustLock(surf) then
+        SDL_UnlockSurface(surf);
+
+    r.x:= 0;
+    r.y:= 0;
+    r.w:= sp^.w;
+    r.h:= sp^.h;
+    ComputeTexcoords(sp, @r, @sp^.tb);
+end;
+
+procedure Repack(var info: AtlasInfo; newAtlas: Atlas);
+var
+    base: PByte;
+    oldSize: Integer;
+    oldWidth: Integer;
+    offset: Integer;
+    i,j : Integer;
+    r: Rectangle;
+    sp: PTexture;
+    newIsRotated: boolean;
+    newSpriteRect: Rectangle;
+begin
+    writeln('Repacking atlas (', info.PackerInfo.width, 'x', info.PackerInfo.height, ')', ' -> (', newAtlas.width, 'x', newAtlas.height, ')');
+
+    // delete the old atlas
+    glDeleteTextures(1, @info.TextureInfo.id);
+
+    // create a new atlas with different size
+    info.TextureInfo:= createTexture(newAtlas.width, newAtlas.height);
+    glBindTexture(GL_TEXTURE_2D, info.TextureInfo.id);
+
+    atlasDelete(info.PackerInfo);
+    info.PackerInfo:= newAtlas;
+
+    // and process all sprites of the new atlas
+    for i:=0 to pred(newAtlas.usedRectangles.count) do
+    begin
+        r:= newAtlas.usedRectangles.data[i];
+        sp:= PTexture(r.UserData);
+        Upload(info, r, sp^.surface);
+    end;
+
+    glBindTexture(GL_TEXTURE_2D, 0);
+end;
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Utility functions
+
+function SizeForSprite(sprite: PTexture): Size;
+begin
+    SizeForSprite.width:= sprite^.w;
+    SizeForSprite.height:= sprite^.h;
+    SizeForSprite.UserData:= sprite;
+end;
+
+procedure EnlargeSize(var x: Integer; var y: Integer);
+begin
+    if (y < x) then
+        y:= y + y
+    else
+        x:= x + x;
+end;
+
+procedure CompactSize(var x: Integer; var y: Integer);
+begin
+    if (x > y) then
+        x:= x div 2
+    else
+        y:= y div 2;
+end;
+
+////////////////////////////////////////////////////////////////////////////////
+// Sprite allocation logic
+
+function TryRepack(var info: AtlasInfo; w, h: Integer; hasNewSprite: boolean; newSprite: Size): boolean;
+var
+    sizes: SizeList;
+    repackedAtlas: Atlas;
+    sprite: PTexture;
+    i: Integer;
+    rects: RectangleList; // we wont really need this as we do a full repack using the atlas later on
+begin
+    TryRepack:= false;
+
+    // STEP 1: collect sizes of all existing sprites
+    sizeListInit(sizes);
+    for i:= 0 to pred(info.PackerInfo.usedRectangles.count) do
+    begin
+        sprite:= PTexture(info.PackerInfo.usedRectangles.data[i].UserData);
+        sizeListAdd(sizes, SizeForSprite(sprite));
+    end;
+
+    // STEP 2: add the new sprite to the list
+    if hasNewSprite then
+        sizeListAdd(sizes, newSprite);
+
+    // STEP 3: try to create a non adaptive re-packing using the whole list
+    repackedAtlas:= atlasNew(w, h);
+    rectangleListInit(rects);
+    if atlasInsertSet(repackedAtlas, sizes, rects) then
+    begin
+        TryRepack:= true;
+        Repack(info, repackedAtlas);
+        // repack assigns repackedAtlas to the current info and deletes the old one
+        // thus we wont do atlasDelete(repackedAtlas); here 
+        rectangleListClear(rects);
+        sizeListClear(sizes);
+        //DumpAtlas(info);
+        exit;
+    end;
+
+    rectangleListClear(rects);
+    sizeListClear(sizes);
+    atlasDelete(repackedAtlas);
+end;
+
+function TryInsert(var info: AtlasInfo; newSprite: Size; surf: PSDL_Surface): boolean;
+var
+    rect: Rectangle;
+    sprite: PTexture;
+begin
+    TryInsert:= false;
+
+    if atlasInsertAdaptive(info.PackerInfo, newSprite, rect) then
+    begin
+        // we succeeded adaptivley allocating the sprite to the i'th atlas.
+        Upload(info, rect, surf);
+        //DumpAtlas(info);
+        TryInsert:= true;
+    end;
+end;
+
+function Surface2Tex_(surf: PSDL_Surface; enableClamp: boolean): PTexture;
+var
+    sz: Size;
+    sprite: PTexture;
+    currentWidth, currentHeight: Integer;
+    i: Integer;
+begin
+    if (surf^.w > MaxTexSize) or (surf^.h > MaxTexSize) then
+    begin
+        // we could at best downscale the sprite, abort for now
+        writeln('Sprite size larger than maximum texture size');
+        halt(-1);        
+    end;
+
+    // allocate the sprite
+    new(sprite);
+    Surface2Tex_:= sprite;
+
+    sprite^.w:= surf^.w;
+    sprite^.h:= surf^.h;
+    sprite^.x:= 0;
+    sprite^.y:= 0;
+    sprite^.isRotated:= false;
+    sprite^.surface:= surf;
+    sprite^.shared:= true;
+    sprite^.nextFrame:= nil;
+
+    sz:= SizeForSprite(sprite);
+
+    // STEP 1
+    // try to allocate the new sprite in one of the existing atlases
+    for i:= 0 to pred(MaxAtlases) do
+    begin
+        if not Info[i].Allocated then
+            continue;
+        if TryInsert(Info[i], sz, surf) then
+            exit;
+    end;
+
+
+    // STEP 2
+    // none of the atlases has space left for the allocation, try a garbage collection
+    for i:= 0 to pred(MaxAtlases) do
+    begin
+        if not Info[i].Allocated then
+            continue;
+
+        if TryRepack(Info[i], Info[i].PackerInfo.width, Info[i].PackerInfo.height, true, sz) then
+            exit;
+    end;
+
+    // STEP 3
+    // none of the atlases could be repacked in a way to fit the new sprite, try enlarging
+    for i:= 0 to pred(MaxAtlases) do
+    begin
+        if not Info[i].Allocated then
+            continue;
+
+        currentWidth:= Info[i].PackerInfo.width;
+        currentHeight:= Info[i].PackerInfo.height;
+
+        EnlargeSize(currentWidth, currentHeight);
+        while (currentWidth <= MaxTexSize) and (currentHeight <= MaxTexSize) do
+        begin
+            if TryRepack(Info[i], currentWidth, currentHeight, true, sz) then
+                exit;
+            EnlargeSize(currentWidth, currentHeight);
+        end;
+    end;
+
+    // STEP 4
+    // none of the existing atlases could be resized, try to allocate a new atlas
+    for i:= 0 to pred(MaxAtlases) do
+    begin
+        if Info[i].Allocated then
+            continue;
+
+        currentWidth:= MinTexSize;
+        currentHeight:= MinTexSize;
+        while (sz.width > currentWidth) do
+            currentWidth:= currentWidth + currentWidth;
+        while (sz.height > currentHeight) do
+            currentHeight:= currentHeight + currentHeight;
+
+        with Info[i] do
+        begin
+            PackerInfo:= atlasNew(currentWidth, currentHeight);
+            TextureInfo:= createTexture(currentWidth, currentHeight);
+            Allocated:= true;
+        end;
+
+        if TryInsert(Info[i], sz, surf) then
+            exit;
+
+        // this shouldnt have happened, the rectpacker should be able to fit the sprite
+        // into an unused rectangle that is the same size or larger than the requested sprite.
+        writeln('Internal error: atlas allocation failed');
+        halt(-1);
+    end;
+
+    // we reached the upperbound of resources we are willing to allocate
+    writeln('Exhausted maximum sprite allocation size');
+    halt(-1);
+end;
+
+////////////////////////////////////////////////////////////////////////////////
+// Sprite deallocation logic
+
+
+procedure FreeTexture_(sprite: PTexture);
+var
+    i, j, deleteAt: Integer;
+    usedArea: Integer;
+    totalArea: Integer;
+    r: Rectangle;
+    atlasW, atlasH: Integer;
+    unused: Size;
+begin
+    if sprite = nil then
+        exit;
+
+    deleteAt:= -1;
+    for i:= 0 to pred(MaxAtlases) do
+    begin
+        if sprite^.atlas <> @Info[i].TextureInfo then
+            continue;
+
+        usedArea:= 0;
+        for j:=0 to pred(Info[i].PackerInfo.usedRectangles.count) do
+        begin
+            r:= Info[i].PackerInfo.usedRectangles.data[j];
+            if r.UserData = sprite then
+                deleteAt:= j
+            else
+                inc(usedArea, r.width * r.height);
+        end;
+
+        rectangleListRemoveAt(Info[i].PackerInfo.usedRectangles, deleteAt);
+        dispose(sprite);
+
+        while true do
+        begin
+            atlasW:= Info[i].PackerInfo.width;
+            atlasH:= Info[i].PackerInfo.height;
+            totalArea:=  atlasW * atlasH;
+            if usedArea >= totalArea * CompressionThreshold then
+                exit;
+
+            if (atlasW = MinTexSize) and (atlasH = MinTexSize) then
+                exit; // we could try to move everything from this to another atlas here
+
+            CompactSize(atlasW, atlasH);
+            unused:= unused;
+            TryRepack(Info[i], atlasW, atlasH, false, unused);
+        end;
+
+        exit;
+    end;
+end;
+
+procedure initModule;
+var
+    i: Integer;
+begin
+    for i:= 0 to pred(MaxAtlases) do
+    begin
+        Info[i].Allocated:= false;
+        Info[i].DumpID:=0;
+    end;
+end;
+
+end.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uBinPacker.pas	Tue Jul 10 11:09:38 2012 +0200
@@ -0,0 +1,438 @@
+unit uBinPacker;
+
+interface
+
+// implements a maxrects packer with best short side fit heuristic
+
+type Rectangle = record
+    x, y, width, height: LongInt;
+    UserData: Pointer;
+end;
+
+type Size = record
+    width, height: LongInt;
+    UserData: Pointer;
+end;
+
+type PRectangle = ^Rectangle;
+type PSize = ^Size;
+
+type RectangleList = record
+    data: PRectangle;
+    count: LongInt;
+    size: LongInt;
+end;
+
+type SizeList = record
+    data: PSize;
+    count: LongInt;
+    size: LongInt;
+end;
+
+type Atlas = record
+    width, height: Longint;
+    freeRectangles: RectangleList;
+    usedRectangles: RectangleList;
+end;
+
+function atlasInsertAdaptive(var a: Atlas; sz: Size; var output: Rectangle): boolean;
+function atlasInsertSet(var a: Atlas; var input: SizeList; var outputs: RectangleList): boolean;
+function atlasNew(width, height: LongInt): Atlas;
+procedure atlasDelete(var a: Atlas);
+procedure atlasReset(var a: Atlas);
+
+procedure rectangleListInit(var list: RectangleList);
+procedure rectangleListRemoveAt(var list: RectangleList; index: LongInt);
+procedure rectangleListAdd(var list: RectangleList; r: Rectangle);
+procedure rectangleListClear(var list: RectangleList);
+procedure sizeListInit(var list: SizeList);
+procedure sizeListRemoveAt(var list: SizeList; index: LongInt);
+procedure sizeListAdd(var list: SizeList; s: Size); overload;
+procedure sizeListAdd(var list: SizeList; width, height: LongInt; UserData: Pointer); overload;
+procedure sizeListClear(var list: SizeList);
+
+implementation
+
+uses Math; // for min/max
+
+procedure rectangleListRemoveAt(var list: RectangleList; index: LongInt);
+var
+    i: Integer;
+begin
+    i:=index;
+    while (i + 1 < list.count) do
+    begin
+        list.data[i]:=list.data[i + 1];
+        inc(i);
+    end;
+    dec(list.count);
+end;
+
+procedure rectangleListAdd(var list: RectangleList; r: Rectangle);
+begin
+    if list.count >= list.size then
+    begin
+        inc(list.size, 512);
+        ReAllocMem(list.data, sizeof(Rectangle) * list.size);
+    end;
+    list.data[list.count]:=r;
+    inc(list.count);
+end;
+
+procedure rectangleListInit(var list: RectangleList);
+begin
+    list.data:= nil;
+    list.count:= 0;
+    list.size:= 0;
+end;
+
+procedure rectangleListClear(var list: RectangleList);
+begin
+    FreeMem(list.data);
+    list.count:= 0;
+    list.size:= 0;
+end;
+
+procedure sizeListRemoveAt(var list: SizeList; index: LongInt);
+begin
+    list.data[index]:= list.data[list.count - 1];
+    dec(list.count);
+end;
+
+procedure sizeListAdd(var list: SizeList; s: Size); overload;
+begin
+    if list.count >= list.size then
+    begin
+        inc(list.size, 512);
+        ReAllocMem(list.data, sizeof(Size) * list.size);
+    end;
+    list.data[list.count]:=s;
+    inc(list.count);
+end;
+
+procedure sizeListAdd(var list: SizeList; width, height: LongInt; UserData: Pointer); overload;
+var
+    sz: Size;
+begin
+    sz.width:= width;
+    sz.height:= height;
+    sz.UserData:= UserData;
+    sizeListAdd(list, sz);
+end;
+
+procedure sizeListInit(var list: SizeList);
+begin
+    list.data:= nil;
+    list.count:= 0;
+    list.size:= 0;
+end;
+
+procedure sizeListClear(var list: SizeList);
+begin
+    FreeMem(list.data);
+    list.count:= 0;
+    list.size:= 0;
+end;
+
+
+function isContainedIn(a, b: Rectangle): boolean;
+begin
+    isContainedIn:= (a.x >= b.x) and (a.y >= b.y)
+                and (a.x + a.width <= b.x + b.width)
+                and (a.y + a.height <= b.y + b.height);
+end;
+
+function findPositionForNewNodeBestShortSideFit(var list: RectangleList; width, height: LongInt; 
+     var bestShortSideFit, bestLongSideFit: LongInt): Rectangle;
+var
+    bestNode: Rectangle;
+    i: Integer;
+    ri: Rectangle;
+    leftoverHoriz, leftoverVert, shortSideFit, longSideFit: Longint;
+begin
+    bestNode.x:= 0;
+    bestNode.y:= 0;
+    bestNode.width:= 0;
+    bestNode.height:= 0;
+    bestShortSideFit:= $7FFFFFFF;
+
+    for i:=0 to pred(list.count) do
+    begin
+        ri:= list.data[i];
+
+        // Try to place the rectangle in upright (non-flipped) orientation.
+        if (ri.width >= width) and (ri.height >= height) then
+        begin
+            leftoverHoriz:= Abs(ri.width - width);
+            leftoverVert:= Abs(ri.height - height);
+            shortSideFit:= Min(leftoverHoriz, leftoverVert);
+            longSideFit:= Max(leftoverHoriz, leftoverVert);
+
+            if (shortSideFit < bestShortSideFit) or
+              ((shortSideFit = bestShortSideFit) and (longSideFit < bestLongSideFit)) then
+            begin
+                bestNode.x:= ri.x;
+                bestNode.y:= ri.y;
+                bestNode.width:= width;
+                bestNode.height:= height;
+                bestShortSideFit:= shortSideFit;
+                bestLongSideFit:= longSideFit;
+            end;
+        end;
+
+        if (ri.width >= height) and (ri.height >= width) then
+        begin
+            leftoverHoriz:= Abs(ri.width - height);
+            leftoverVert:= Abs(ri.height - width);
+            shortSideFit:= Min(leftoverHoriz, leftoverVert);
+            longSideFit:= Max(leftoverHoriz, leftoverVert);
+
+            if (shortSideFit < bestShortSideFit) or
+              ((shortSideFit = bestShortSideFit) and (longSideFit < bestLongSideFit)) then
+            begin
+                bestNode.x:= ri.x;
+                bestNode.y:= ri.y;
+                bestNode.width:= height;
+                bestNode.height:= width;
+                bestShortSideFit:= shortSideFit;
+                bestLongSideFit:= longSideFit;
+            end;
+        end;
+    end;
+
+    findPositionForNewNodeBestShortSideFit:= bestNode;
+end;
+
+function scoreRect(var list: RectangleList; width, height: LongInt; var score1, score2: LongInt): Rectangle;
+var
+    newNode: Rectangle;
+begin
+    newNode:= findPositionForNewNodeBestShortSideFit(list, width, height, score1, score2);
+
+    // Cannot fit the current rectangle.
+    if newNode.height = 0 then
+    begin
+        score1:= $7FFFFFFF;
+        score2:= $7FFFFFFF;
+    end;
+
+    scoreRect:= newNode;
+end;
+
+function splitFreeNode(var freeRectangles: RectangleList; freeNode, usedNode: Rectangle): boolean;
+var
+    newNode: Rectangle;
+begin
+    // Test with SAT if the rectangles even intersect.
+    if (usedNode.x >= freeNode.x + freeNode.width) or (usedNode.x + usedNode.width <= freeNode.x) or
+       (usedNode.y >= freeNode.y + freeNode.height) or (usedNode.y + usedNode.height <= freeNode.y) then
+    begin
+        splitFreeNode:=false;
+        exit;
+    end;
+
+    if (usedNode.x < freeNode.x + freeNode.width) and (usedNode.x + usedNode.width > freeNode.x) then
+    begin
+        // New node at the top side of the used node.
+        if (usedNode.y > freeNode.y) and (usedNode.y < freeNode.y + freeNode.height) then
+        begin
+            newNode:= freeNode;
+            newNode.height:= usedNode.y - newNode.y;
+            rectangleListAdd(freeRectangles, newNode);
+        end;
+
+        // New node at the bottom side of the used node.
+        if (usedNode.y + usedNode.height < freeNode.y + freeNode.height) then
+        begin
+            newNode:= freeNode;
+            newNode.y:= usedNode.y + usedNode.height;
+            newNode.height:= freeNode.y + freeNode.height - (usedNode.y + usedNode.height);
+            rectangleListAdd(freeRectangles, newNode);
+        end;
+    end;
+
+    if (usedNode.y < freeNode.y + freeNode.height) and (usedNode.y + usedNode.height > freeNode.y) then
+    begin
+        // New node at the left side of the used node.
+        if (usedNode.x > freeNode.x) and (usedNode.y < freeNode.y + freeNode.width) then
+        begin
+            newNode:= freeNode;
+            newNode.width:= usedNode.x - newNode.x;
+            rectangleListAdd(freeRectangles, newNode);
+        end;
+
+        // New node at the right side of the used node.
+        if (usedNode.x + usedNode.width < freeNode.x + freeNode.width) then
+        begin
+            newNode:= freeNode;
+            newNode.x:= usedNode.x + usedNode.width;
+            newNode.width:= freeNode.x + freeNode.width - (usedNode.x + usedNode.width);
+            rectangleListAdd(freeRectangles, newNode);
+        end;
+  end;
+
+  splitFreeNode:= true;
+end;
+
+procedure pruneFreeList(var freeRectangles: RectangleList);
+var
+  i, j: LongInt;
+begin
+    // Go through each pair and remove any rectangle that is redundant.
+    i:= 0;
+    while i < freeRectangles.count do
+    begin
+        j:= i + 1;
+        while j < freeRectangles.count do
+        begin  
+            if (isContainedIn(freeRectangles.data[i], freeRectangles.data[j])) then
+            begin
+                rectangleListRemoveAt(freeRectangles, i);
+                dec(i);
+                break;
+            end;
+
+            if (isContainedIn(freeRectangles.data[j], freeRectangles.data[i])) then
+                rectangleListRemoveAt(freeRectangles, j)
+            else
+                inc(j);
+        end;
+        inc(i);
+    end;
+end;
+
+function atlasInsertAdaptive(var a: Atlas; sz: Size; var output: Rectangle): boolean;
+var
+    newNode: Rectangle;
+    score1, score2: LongInt;
+    numRectanglesToProcess: LongInt;
+    i: LongInt;
+begin
+    newNode:= findPositionForNewNodeBestShortSideFit(a.freeRectangles, sz.width, sz.height, score1, score2);
+    if newNode.height = 0 then
+    begin
+        output:= newNode;
+        output.UserData:= nil;
+        atlasInsertAdaptive:= false;
+        exit;
+    end;
+
+    numRectanglesToProcess:= a.freeRectangles.count;
+
+    i:=0;
+    while i < numRectanglesToProcess do
+    begin
+        if splitFreeNode(a.freeRectangles, a.freeRectangles.data[i], newNode) then
+        begin
+            rectangleListRemoveAt(a.freeRectangles, i);
+            dec(numRectanglesToProcess);
+        end
+        else
+            inc(i);
+    end;
+    
+    pruneFreeList(a.freeRectangles);
+    newNode.UserData:= sz.UserData;
+    rectangleListAdd(a.usedRectangles, newNode);
+    output:= newNode;
+    atlasInsertAdaptive:= true;
+end;
+
+procedure placeRect(var a: Atlas; node: Rectangle);
+var
+    numRectanglesToProcess: LongInt;
+    i: LongInt;
+begin
+    numRectanglesToProcess:= a.freeRectangles.Count;
+
+    i:= 0;
+    while i < numRectanglesToProcess do
+    begin
+        if not splitFreeNode(a.freeRectangles, a.freeRectangles.data[i], node) then
+            inc(i)
+        else
+        begin
+            rectangleListRemoveAt(a.freeRectangles, i);
+            dec(numRectanglesToProcess);
+        end;
+    end;
+
+    pruneFreeList(a.freeRectangles);
+    rectangleListAdd(a.usedRectangles, node);
+end;
+
+
+function atlasInsertSet(var a: Atlas; var input: SizeList; var outputs: RectangleList): boolean;
+var
+    bestScore1, bestScore2, bestRectIndex: LongInt;
+    score1, score2: LongInt;
+    bestNode, newNode: Rectangle;
+    i: LongInt;
+    sz: Size;
+begin
+    atlasInsertSet:= false;
+
+    while input.count > 0 do
+    begin
+        bestScore1:= $7FFFFFFF;
+        bestScore2:= $7FFFFFFF;
+        bestRectIndex:= -1;
+    
+        for i:=0 to pred(input.count) do
+        begin
+            sz:= input.data[i];
+            newNode:= scoreRect(a.freeRectangles, sz.width, sz.height, score1, score2);
+
+            if (score1 >= bestScore1) and ((score1 <> bestScore1) or (score2 >= bestScore2)) then
+                continue;
+
+            bestScore1:= score1;
+            bestScore2:= score2;
+            bestNode:= newNode;
+            bestRectIndex:= i;
+        end;
+
+        if bestRectIndex = -1 then
+            exit;
+
+        bestNode.UserData:= input.data[bestRectIndex].UserData;
+        placeRect(a, bestNode);
+        rectangleListAdd(outputs, bestNode);
+        sizeListRemoveAt(input, bestRectIndex);
+    end;
+    atlasInsertSet:= true;
+end;
+
+function atlasNew(width, height: LongInt): Atlas;
+var
+    a: Atlas;
+    r: Rectangle;
+begin
+    rectangleListInit(a.freeRectangles);
+    rectangleListInit(a.usedRectangles);
+
+    a.width:= width;
+    a.height:= height;
+    r.x:= 0;
+    r.y:= 0;
+    r.width:= width;
+    r.height:= height;
+    rectangleListAdd(a.freeRectangles, r);
+
+    atlasNew:=a;
+end;
+
+procedure atlasDelete(var a: atlas);
+begin
+    rectangleListClear(a.freeRectangles);
+    rectangleListClear(a.usedRectangles);
+end;
+
+procedure atlasReset(var a: atlas);
+begin
+    atlasDelete(a);
+    a:=atlasNew(a.width, a.height);
+end;
+
+begin
+end.
--- a/hedgewars/uChat.pas	Mon Jul 09 23:28:02 2012 -0400
+++ b/hedgewars/uChat.pas	Tue Jul 10 11:09:38 2012 +0200
@@ -31,7 +31,7 @@
 procedure KeyPressChat(Key: Longword);
 
 implementation
-uses SDLh, uInputHandler, uTypes, uVariables, uCommands, uUtils, uTextures, uRender, uIO;
+uses SDLh, uInputHandler, uTypes, uVariables, uCommands, uUtils, uTextures, uRender, uStore, uIO;
 
 const MaxStrIndex = 27;
 
@@ -96,9 +96,7 @@
 SDL_FreeSurface(strSurface);
 
 cl.Time:= RealTicks + 12500;
-cl.Tex:= Surface2Tex(resSurface, false);
-
-SDL_FreeSurface(resSurface)
+cl.Tex:= Surface2Atlas(resSurface, false);
 end;
 
 // For uStore texture recreation
--- a/hedgewars/uGearsRender.pas	Mon Jul 09 23:28:02 2012 -0400
+++ b/hedgewars/uGearsRender.pas	Tue Jul 10 11:09:38 2012 +0200
@@ -37,7 +37,7 @@
                 end;
 
 implementation
-uses uRender, uUtils, uVariables, uAmmos, Math, uVisualGears;
+uses uRender, uStore, uUtils, uVariables, uAmmos, Math, uVisualGears;
 
 procedure DrawRopeLinesRQ(Gear: PGear);
 begin
@@ -54,20 +54,18 @@
     glDisable(GL_TEXTURE_2D);
     //glEnable(GL_LINE_SMOOTH);
 
-    glPushMatrix;
-
-    glTranslatef(WorldDx, WorldDy, 0);
+    ResetRotation;
+    SetOffset(WorldDx, WorldDy);
+    UpdateModelview;
 
     glLineWidth(4.0);
 
     Tint($C0, $C0, $C0, $FF);
 
-    glVertexPointer(2, GL_FLOAT, 0, @RopePoints.rounded[0]);
+    SetVertexPointer(@RopePoints.rounded[0]);
     glDrawArrays(GL_LINE_STRIP, 0, RopePoints.Count + 2);
     Tint($FF, $FF, $FF, $FF);
 
-    glPopMatrix;
-
     glEnable(GL_TEXTURE_2D);
     //glDisable(GL_LINE_SMOOTH)
     end
@@ -1036,7 +1034,7 @@
                     else
                         DrawSpriteRotatedF(sprExplosivesRoll, x, y + 4, 1, 0, Gear^.DirAngle);
                     end;
-        gtDynamite: DrawSprite(sprDynamite, x - 16, y - 25, Gear^.Tag and 1, Gear^.Tag shr 1);
+        gtDynamite: begin writeln(stdout, 'FIXME'); halt(-3); end; //DrawSprite(sprDynamite, x - 16, y - 25, Gear^.Tag and 1, Gear^.Tag shr 1);
      gtClusterBomb: DrawSpriteRotated(sprClusterBomb, x, y, 0, Gear^.DirAngle);
          gtCluster: DrawSprite(sprClusterParticle, x - 8, y - 8, 0);
            gtFlame: if Gear^.Tag and 1 = 0 then
--- a/hedgewars/uLandTexture.pas	Mon Jul 09 23:28:02 2012 -0400
+++ b/hedgewars/uLandTexture.pas	Tue Jul 10 11:09:38 2012 +0200
@@ -99,7 +99,7 @@
             with LandTextures[x, y] do
                 begin
                 tex:= NewTexture(TEXSIZE, TEXSIZE, Pixels(x, y));
-                glBindTexture(GL_TEXTURE_2D, tex^.id);
+                glBindTexture(GL_TEXTURE_2D, tex^.atlas^.id);
                 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_PRIORITY, tpHigh);
                 end
 else
@@ -156,7 +156,7 @@
                     if not isEmpty then
                         begin
                         if tex = nil then tex:= NewTexture(TEXSIZE, TEXSIZE, Pixels(x, y));
-                        glBindTexture(GL_TEXTURE_2D, tex^.id);
+                        glBindTexture(GL_TEXTURE_2D, tex^.atlas^.id);
                         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TEXSIZE, TEXSIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, Pixels(x,y));
                         end
                     else if tex <> nil then
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uMatrix.pas	Tue Jul 10 11:09:38 2012 +0200
@@ -0,0 +1,123 @@
+(*
+ * Hedgewars, a free turn based strategy game
+ * Copyright (c) 2004-2012 Andrey Korotaev <unC0Rr@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ *)
+
+{$INCLUDE "options.inc"}
+
+unit uMatrix;
+
+interface
+
+uses uTypes, gl;
+
+procedure MatrixLoadIdentity(out Result: TMatrix4x4f);
+procedure MatrixMultiply(out Result: TMatrix4x4f; const lhs, rhs: TMatrix4x4f);
+
+implementation
+
+procedure MatrixLoadIdentity(out Result: TMatrix4x4f);
+begin
+    Result[0,0]:= 1.0; Result[1,0]:=0.0; Result[2,0]:=0.0; Result[3,0]:=0.0;
+    Result[0,1]:= 0.0; Result[1,1]:=1.0; Result[2,1]:=0.0; Result[3,1]:=0.0;
+    Result[0,2]:= 0.0; Result[1,2]:=0.0; Result[2,2]:=1.0; Result[3,2]:=0.0;
+    Result[0,3]:= 0.0; Result[1,3]:=0.0; Result[2,3]:=0.0; Result[3,3]:=1.0;
+end;
+
+procedure MatrixMultiply(out Result: TMatrix4x4f; const lhs, rhs: TMatrix4x4f);
+var
+    test: TMatrix4x4f;
+    i, j: Integer;
+    error: boolean;
+begin
+    Result[0,0]:=lhs[0,0]*rhs[0,0] + lhs[1,0]*rhs[0,1] + lhs[2,0]*rhs[0,2] + lhs[3,0]*rhs[0,3];
+    Result[0,1]:=lhs[0,1]*rhs[0,0] + lhs[1,1]*rhs[0,1] + lhs[2,1]*rhs[0,2] + lhs[3,1]*rhs[0,3];
+    Result[0,2]:=lhs[0,2]*rhs[0,0] + lhs[1,2]*rhs[0,1] + lhs[2,2]*rhs[0,2] + lhs[3,2]*rhs[0,3];
+    Result[0,3]:=lhs[0,3]*rhs[0,0] + lhs[1,3]*rhs[0,1] + lhs[2,3]*rhs[0,2] + lhs[3,3]*rhs[0,3];
+
+    Result[1,0]:=lhs[0,0]*rhs[1,0] + lhs[1,0]*rhs[1,1] + lhs[2,0]*rhs[1,2] + lhs[3,0]*rhs[1,3];
+    Result[1,1]:=lhs[0,1]*rhs[1,0] + lhs[1,1]*rhs[1,1] + lhs[2,1]*rhs[1,2] + lhs[3,1]*rhs[1,3];
+    Result[1,2]:=lhs[0,2]*rhs[1,0] + lhs[1,2]*rhs[1,1] + lhs[2,2]*rhs[1,2] + lhs[3,2]*rhs[1,3];
+    Result[1,3]:=lhs[0,3]*rhs[1,0] + lhs[1,3]*rhs[1,1] + lhs[2,3]*rhs[1,2] + lhs[3,3]*rhs[1,3];
+
+    Result[2,0]:=lhs[0,0]*rhs[2,0] + lhs[1,0]*rhs[2,1] + lhs[2,0]*rhs[2,2] + lhs[3,0]*rhs[2,3];
+    Result[2,1]:=lhs[0,1]*rhs[2,0] + lhs[1,1]*rhs[2,1] + lhs[2,1]*rhs[2,2] + lhs[3,1]*rhs[2,3];
+    Result[2,2]:=lhs[0,2]*rhs[2,0] + lhs[1,2]*rhs[2,1] + lhs[2,2]*rhs[2,2] + lhs[3,2]*rhs[2,3];
+    Result[2,3]:=lhs[0,3]*rhs[2,0] + lhs[1,3]*rhs[2,1] + lhs[2,3]*rhs[2,2] + lhs[3,3]*rhs[2,3];
+
+    Result[3,0]:=lhs[0,0]*rhs[3,0] + lhs[1,0]*rhs[3,1] + lhs[2,0]*rhs[3,2] + lhs[3,0]*rhs[3,3];
+    Result[3,1]:=lhs[0,1]*rhs[3,0] + lhs[1,1]*rhs[3,1] + lhs[2,1]*rhs[3,2] + lhs[3,1]*rhs[3,3];
+    Result[3,2]:=lhs[0,2]*rhs[3,0] + lhs[1,2]*rhs[3,1] + lhs[2,2]*rhs[3,2] + lhs[3,2]*rhs[3,3];
+    Result[3,3]:=lhs[0,3]*rhs[3,0] + lhs[1,3]*rhs[3,1] + lhs[2,3]*rhs[3,2] + lhs[3,3]*rhs[3,3];
+
+{
+    Result[0,0]:=lhs[0,0]*rhs[0,0] + lhs[1,0]*rhs[0,1] + lhs[2,0]*rhs[0,2] + lhs[3,0]*rhs[0,3];
+    Result[0,1]:=lhs[0,0]*rhs[1,0] + lhs[1,0]*rhs[1,1] + lhs[2,0]*rhs[1,2] + lhs[3,0]*rhs[1,3];
+    Result[0,2]:=lhs[0,0]*rhs[2,0] + lhs[1,0]*rhs[2,1] + lhs[2,0]*rhs[2,2] + lhs[3,0]*rhs[2,3];
+    Result[0,3]:=lhs[0,0]*rhs[3,0] + lhs[1,0]*rhs[3,1] + lhs[2,0]*rhs[3,2] + lhs[3,0]*rhs[3,3];
+  
+    Result[1,0]:=lhs[0,1]*rhs[0,0] + lhs[1,1]*rhs[0,1] + lhs[2,1]*rhs[0,2] + lhs[3,1]*rhs[0,3];
+    Result[1,1]:=lhs[0,1]*rhs[1,0] + lhs[1,1]*rhs[1,1] + lhs[2,1]*rhs[1,2] + lhs[3,1]*rhs[1,3];
+    Result[1,2]:=lhs[0,1]*rhs[2,0] + lhs[1,1]*rhs[2,1] + lhs[2,1]*rhs[2,2] + lhs[3,1]*rhs[2,3];
+    Result[1,3]:=lhs[0,1]*rhs[3,0] + lhs[1,1]*rhs[3,1] + lhs[2,1]*rhs[3,2] + lhs[3,1]*rhs[3,3];
+
+    Result[2,0]:=lhs[0,2]*rhs[0,0] + lhs[1,2]*rhs[0,1] + lhs[2,2]*rhs[0,2] + lhs[3,2]*rhs[0,3];
+    Result[2,1]:=lhs[0,2]*rhs[1,0] + lhs[1,2]*rhs[1,1] + lhs[2,2]*rhs[1,2] + lhs[3,2]*rhs[1,3];
+    Result[2,2]:=lhs[0,2]*rhs[2,0] + lhs[1,2]*rhs[2,1] + lhs[2,2]*rhs[2,2] + lhs[3,2]*rhs[2,3];
+    Result[2,3]:=lhs[0,2]*rhs[3,0] + lhs[1,2]*rhs[3,1] + lhs[2,2]*rhs[3,2] + lhs[3,2]*rhs[3,3];
+
+    Result[3,0]:=lhs[0,3]*rhs[0,0] + lhs[1,3]*rhs[0,1] + lhs[2,3]*rhs[0,2] + lhs[3,3]*rhs[0,3];
+    Result[3,1]:=lhs[0,3]*rhs[1,0] + lhs[1,3]*rhs[1,1] + lhs[2,3]*rhs[1,2] + lhs[3,3]*rhs[1,3];
+    Result[3,2]:=lhs[0,3]*rhs[2,0] + lhs[1,3]*rhs[2,1] + lhs[2,3]*rhs[2,2] + lhs[3,3]*rhs[2,3];
+    Result[3,3]:=lhs[0,3]*rhs[3,0] + lhs[1,3]*rhs[3,1] + lhs[2,3]*rhs[3,2] + lhs[3,3]*rhs[3,3];
+}
+    glPushMatrix;
+    glLoadMatrixf(@lhs[0, 0]);
+    glMultMatrixf(@rhs[0, 0]);
+    glGetFloatv(GL_MODELVIEW_MATRIX, @test[0, 0]);
+    glPopMatrix;
+
+    error:=false;
+    for i:=0 to 3 do
+      for j:=0 to 3 do
+        if Abs(test[i, j] - Result[i, j]) > 0.000001 then
+          error:=true;
+
+    if error then
+    begin
+        writeln('shall:');
+        for i:=0 to 3 do
+        begin
+          for j:=0 to 3 do
+            write(test[i, j]);
+          writeln;
+        end;
+
+        writeln('is:');
+        for i:=0 to 3 do
+        begin
+          for j:=0 to 3 do
+            write(Result[i, j]);
+          writeln;
+        end;
+        halt(0);
+    end;
+
+
+end;
+
+
+end.
--- a/hedgewars/uRender.pas	Mon Jul 09 23:28:02 2012 -0400
+++ b/hedgewars/uRender.pas	Tue Jul 10 11:09:38 2012 +0200
@@ -22,10 +22,9 @@
 
 interface
 
-uses SDLh, uTypes, GLunit, uConsts;
+uses SDLh, uTypes, GLunit, uConsts, uTextures, math;
 
 procedure DrawSprite            (Sprite: TSprite; X, Y, Frame: LongInt);
-procedure DrawSprite            (Sprite: TSprite; X, Y, FrameX, FrameY: LongInt);
 procedure DrawSpriteFromRect    (Sprite: TSprite; r: TSDL_Rect; X, Y, Height, Position: LongInt);
 procedure DrawSpriteClipped     (Sprite: TSprite; X, Y, TopY, RightX, BottomY, LeftX: LongInt);
 procedure DrawSpriteRotated     (Sprite: TSprite; X, Y, Dir: LongInt; Angle: real);
@@ -48,14 +47,78 @@
 procedure DrawHedgehog          (X, Y: LongInt; Dir: LongInt; Pos, Step: LongWord; Angle: real);
 procedure DrawScreenWidget      (widget: POnScreenWidget);
 
-procedure Tint                  (r, g, b, a: Byte); inline;
-procedure Tint                  (c: Longword); inline;
+// This is just temporary and becomes non public once everything changed to GL2
+procedure UpdateModelview;
+procedure ResetModelview;
+procedure SetOffset(X, Y: Longint);
+procedure ResetRotation;
 
 
 implementation
-uses uVariables;
+uses uVariables, uStore;
+
+const DegToRad =  0.01745329252; // 2PI / 360
+
+procedure UpdateModelview;
+begin
+{$IFDEF GL2}
+    UpdateModelviewProjection;
+{$ELSE}
+    glLoadMatrixf(@mModelview[0,0]);
+{$ENDIF}
+end;
+
+procedure ResetModelview;
+begin
+    mModelview[0,0]:= 1.0; mModelview[1,0]:=0.0; mModelview[3,0]:= 0;
+    mModelview[0,1]:= 0.0; mModelview[1,1]:=1.0; mModelview[3,1]:= 0;
+    UpdateModelview;
+end;
+
+procedure SetOffset(X, Y: Longint);
+begin
+    mModelview[3,0]:= X;
+    mModelview[3,1]:= Y;
+end;
+
+procedure AddOffset(X, Y: GLfloat); // probably want to refactor this to use integers
+begin
+    mModelview[3,0]:=mModelview[3,0] + mModelview[0,0]*X + mModelview[1,0]*Y;
+    mModelview[3,1]:=mModelview[3,1] + mModelview[0,1]*X + mModelview[1,1]*Y;
+end;
 
-var LastTint: LongWord = 0;
+procedure SetScale(Scale: GLfloat);
+begin
+    mModelview[0,0]:= Scale;
+    mModelview[1,1]:= Scale;
+end;
+
+procedure AddScale(Scale: GLfloat);
+begin
+    mModelview[0,0]:= mModelview[0,0]*Scale; mModelview[1,0]:= mModelview[1,0]*Scale;
+    mModelview[0,1]:= mModelview[0,1]*Scale; mModelview[1,1]:= mModelview[1,1]*Scale;
+end;
+
+procedure AddScale(X, Y: GLfloat);
+begin
+    mModelview[0,0]:= mModelview[0,0]*X; mModelview[1,0]:= mModelview[1,0]*Y;
+    mModelview[0,1]:= mModelview[0,1]*X; mModelview[1,1]:= mModelview[1,1]*Y;
+end;
+
+
+procedure SetRotation(Angle, ZAxis: GLfloat);
+var s, c: Extended;
+begin
+    SinCos(Angle*DegToRad, s, c);
+    mModelview[0,0]:= c;       mModelview[1,0]:=-s*ZAxis;
+    mModelview[0,1]:= s*ZAxis; mModelview[1,1]:= c;
+end;
+
+procedure ResetRotation;
+begin
+    mModelview[0,0]:= 1.0; mModelview[1,0]:=0.0;
+    mModelview[0,1]:= 0.0; mModelview[1,1]:=1.0;
+end;
 
 procedure DrawSpriteFromRect(Sprite: TSprite; r: TSDL_Rect; X, Y, Height, Position: LongInt);
 begin
@@ -70,52 +133,48 @@
 end;
 
 procedure DrawTextureFromRect(X, Y, W, H: LongInt; r: PSDL_Rect; SourceTexture: PTexture);
-var rr: TSDL_Rect;
-    _l, _r, _t, _b: real;
-    VertexBuffer, TextureBuffer: array [0..3] of TVertex2f;
+var 
+    rr: TSDL_Rect;
+    VertexBuffer, TextureBuffer: TVertexRect;
+    _l, _r, _t, _b: GLfloat;
 begin
-if (SourceTexture^.h = 0) or (SourceTexture^.w = 0) then
-    exit;
+    if (SourceTexture^.h = 0) or (SourceTexture^.w = 0) then
+        exit;
 
-// do not draw anything outside the visible screen space (first check fixes some sprite drawing, e.g. hedgehogs)
-if (abs(X) > W) and ((abs(X + W / 2) - W / 2) > cScreenWidth / cScaleFactor) then
-    exit;
-if (abs(Y) > H) and ((abs(Y + H / 2 - (0.5 * cScreenHeight)) - H / 2) > cScreenHeight / cScaleFactor) then
-    exit;
+    // do not draw anything outside the visible screen space (first check fixes some sprite drawing, e.g. hedgehogs)
+    if (abs(X) > W) and ((abs(X + W / 2) - W / 2) > cScreenWidth / cScaleFactor) then
+        exit;
+    if (abs(Y) > H) and ((abs(Y + H / 2 - (0.5 * cScreenHeight)) - H / 2) > cScreenHeight / cScaleFactor) then
+        exit;
 
-rr.x:= X;
-rr.y:= Y;
-rr.w:= W;
-rr.h:= H;
+    rr.x:= X;
+    rr.y:= Y;
+    rr.w:= W;
+    rr.h:= H;
 
-_l:= r^.x / SourceTexture^.w * SourceTexture^.rx;
-_r:= (r^.x + r^.w) / SourceTexture^.w * SourceTexture^.rx;
-_t:= r^.y / SourceTexture^.h * SourceTexture^.ry;
-_b:= (r^.y + r^.h) / SourceTexture^.h * SourceTexture^.ry;
+    glBindTexture(GL_TEXTURE_2D, SourceTexture^.atlas^.id);
 
-glBindTexture(GL_TEXTURE_2D, SourceTexture^.id);
+    ComputeTexcoords(SourceTexture, r, @TextureBuffer);
 
-VertexBuffer[0].X:= X;
-VertexBuffer[0].Y:= Y;
-VertexBuffer[1].X:= rr.w + X;
-VertexBuffer[1].Y:= Y;
-VertexBuffer[2].X:= rr.w + X;
-VertexBuffer[2].Y:= rr.h + Y;
-VertexBuffer[3].X:= X;
-VertexBuffer[3].Y:= rr.h + Y;
+    _l:= X + SourceTexture^.cropInfo.l;
+    _r:= X + rr.w - SourceTexture^.cropInfo.l - SourceTexture^.cropInfo.r;
+    _t:= Y + SourceTexture^.cropInfo.t;
+    _b:= Y + rr.h - SourceTexture^.cropInfo.t - SourceTexture^.cropInfo.b;
+
 
-TextureBuffer[0].X:= _l;
-TextureBuffer[0].Y:= _t;
-TextureBuffer[1].X:= _r;
-TextureBuffer[1].Y:= _t;
-TextureBuffer[2].X:= _r;
-TextureBuffer[2].Y:= _b;
-TextureBuffer[3].X:= _l;
-TextureBuffer[3].Y:= _b;
+    VertexBuffer[0].X:= _l;
+    VertexBuffer[0].Y:= _t;
+    VertexBuffer[1].X:= _r;
+    VertexBuffer[1].Y:= _t;
+    VertexBuffer[2].X:= _r;
+    VertexBuffer[2].Y:= _b;
+    VertexBuffer[3].X:= _l;
+    VertexBuffer[3].Y:= _b;
 
-glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]);
-glTexCoordPointer(2, GL_FLOAT, 0, @TextureBuffer[0]);
-glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer));
+    SetVertexPointer(@VertexBuffer[0]);
+    //SetTexCoordPointer(@TextureBuffer[0]);
+    SetTexCoordPointer(@SourceTexture^.tb[0]);
+    glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer));
 end;
 
 procedure DrawTexture(X, Y: LongInt; Texture: PTexture); inline;
@@ -125,18 +184,17 @@
 
 procedure DrawTexture(X, Y: LongInt; Texture: PTexture; Scale: GLfloat);
 begin
-
-glPushMatrix;
-glTranslatef(X, Y, 0);
-glScalef(Scale, Scale, 1);
+SetOffset(X, Y);
+ResetRotation;
+SetScale(Scale);
+UpdateModelview;
 
-glBindTexture(GL_TEXTURE_2D, Texture^.id);
+glBindTexture(GL_TEXTURE_2D, Texture^.atlas^.id);
 
-glVertexPointer(2, GL_FLOAT, 0, @Texture^.vb);
-glTexCoordPointer(2, GL_FLOAT, 0, @Texture^.tb);
+SetVertexPointer(@Texture^.vb);
+SetTexCoordPointer(@Texture^.tb);
 glDrawArrays(GL_TRIANGLE_FAN, 0, Length(Texture^.vb));
-
-glPopMatrix
+ResetModelview;
 end;
 
 procedure DrawTextureF(Texture: PTexture; Scale: GLfloat; X, Y, Frame, Dir, w, h: LongInt);
@@ -145,63 +203,68 @@
 end;
 
 procedure DrawTextureRotatedF(Texture: PTexture; Scale, OffsetX, OffsetY: GLfloat; X, Y, Frame, Dir, w, h: LongInt; Angle: real);
-var ft, fb, fl, fr: GLfloat;
-    hw, nx, ny: LongInt;
+var hw, nx, ny: LongInt;
+    r: TSDL_Rect;
     VertexBuffer, TextureBuffer: array [0..3] of TVertex2f;
+    _l, _r, _t, _b: GLfloat;
 begin
+
+    while (Frame > 0) and (Texture <> nil) do
+    begin
+        Texture:= Texture^.nextFrame;
+        dec(Frame);
+    end;
+
+    if Texture = nil then
+        exit;
+
 // do not draw anything outside the visible screen space (first check fixes some sprite drawing, e.g. hedgehogs)
 if (abs(X) > W) and ((abs(X + dir * OffsetX) - W / 2) * cScaleFactor > cScreenWidth) then
     exit;
 if (abs(Y) > H) and ((abs(Y + OffsetY - (0.5 * cScreenHeight)) - W / 2) * cScaleFactor > cScreenHeight) then
     exit;
 
-glPushMatrix;
-glTranslatef(X, Y, 0);
+SetOffset(X, Y);
 if Dir = 0 then Dir:= 1;
 
-glRotatef(Angle, 0, 0, Dir);
-
-glTranslatef(Dir*OffsetX, OffsetY, 0);
-glScalef(Scale, Scale, 1);
+SetRotation(Angle, Dir);
+AddOffset(Dir*OffsetX, OffsetY);
+AddScale(Scale);
+UpdateModelview;
 
 // Any reason for this call? And why only in t direction, not s?
 //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 
 hw:= w div (2 div Dir);
 
-nx:= round(Texture^.w / w); // number of horizontal frames
-ny:= round(Texture^.h / h); // number of vertical frames
+r.y:=0;
+r.x:=0;
+r.w:=w;
+r.h:=h;
+ComputeTexcoords(Texture, @r, @TextureBuffer);
 
-ft:= (Frame mod ny) * Texture^.ry / ny;
-fb:= ((Frame mod ny) + 1) * Texture^.ry / ny;
-fl:= (Frame div ny) * Texture^.rx / nx;
-fr:= ((Frame div ny) + 1) * Texture^.rx / nx;
+glBindTexture(GL_TEXTURE_2D, Texture^.atlas^.id);
 
-glBindTexture(GL_TEXTURE_2D, Texture^.id);
+_l:= -hw + Texture^.cropInfo.l;
+_t:= w/-2 + Texture^.cropInfo.t;
+_r:= hw - Texture^.cropInfo.l - Texture^.cropInfo.r;
+_b:= w/2 - Texture^.cropInfo.t - Texture^.cropInfo.b;
 
-VertexBuffer[0].X:= -hw;
-VertexBuffer[0].Y:= w / -2;
-VertexBuffer[1].X:= hw;
-VertexBuffer[1].Y:= w / -2;
-VertexBuffer[2].X:= hw;
-VertexBuffer[2].Y:= w / 2;
-VertexBuffer[3].X:= -hw;
-VertexBuffer[3].Y:= w / 2;
+VertexBuffer[0].X:= _l;
+VertexBuffer[0].Y:= _t;
+VertexBuffer[1].X:= _r;
+VertexBuffer[1].Y:= _t;
+VertexBuffer[2].X:= _r;
+VertexBuffer[2].Y:= _b;
+VertexBuffer[3].X:= _l;
+VertexBuffer[3].Y:= _b;
 
-TextureBuffer[0].X:= fl;
-TextureBuffer[0].Y:= ft;
-TextureBuffer[1].X:= fr;
-TextureBuffer[1].Y:= ft;
-TextureBuffer[2].X:= fr;
-TextureBuffer[2].Y:= fb;
-TextureBuffer[3].X:= fl;
-TextureBuffer[3].Y:= fb;
-
-glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]);
-glTexCoordPointer(2, GL_FLOAT, 0, @TextureBuffer[0]);
+SetVertexPointer(@VertexBuffer[0]);
+//SetTexCoordPointer(@TextureBuffer[0]);
+SetTexCoordPointer(@Texture^.tb[0]);
 glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer));
 
-glPopMatrix
+ResetModelview;
 end;
 
 procedure DrawSpriteRotated(Sprite: TSprite; X, Y, Dir: LongInt; Angle: real);
@@ -214,23 +277,23 @@
 
 procedure DrawSpriteRotatedF(Sprite: TSprite; X, Y, Frame, Dir: LongInt; Angle: real);
 begin
-glPushMatrix;
-glTranslatef(X, Y, 0);
-
+SetOffset(X, Y);
 if Dir < 0 then
-    glRotatef(Angle, 0, 0, -1)
+    SetRotation(Angle, -1.0)
 else
-    glRotatef(Angle, 0, 0,  1);
+    SetRotation(Angle, 1.0);
 if Dir < 0 then
-    glScalef(-1.0, 1.0, 1.0);
+    AddScale(-1.0, 1.0);
+UpdateModelview;
 
 DrawSprite(Sprite, -SpritesData[Sprite].Width div 2, -SpritesData[Sprite].Height div 2, Frame);
 
-glPopMatrix
+ResetModelview;
 end;
 
 procedure DrawTextureRotated(Texture: PTexture; hw, hh, X, Y, Dir: LongInt; Angle: real);
 var VertexBuffer: array [0..3] of TVertex2f;
+    _l, _r, _t, _b: GLfloat;
 begin
 // do not draw anything outside the visible screen space (first check fixes some sprite drawing, e.g. hedgehogs)
 if (abs(X) > 2 * hw) and ((abs(X) - hw) > cScreenWidth / cScaleFactor) then
@@ -238,55 +301,64 @@
 if (abs(Y) > 2 * hh) and ((abs(Y - 0.5 * cScreenHeight) - hh) > cScreenHeight / cScaleFactor) then
     exit;
 
-glPushMatrix;
-glTranslatef(X, Y, 0);
+SetOffset(X, Y);
 
 if Dir < 0 then
     begin
     hw:= - hw;
-    glRotatef(Angle, 0, 0, -1);
+    SetRotation(Angle, -1.0);
     end
 else
-    glRotatef(Angle, 0, 0,  1);
+    SetRotation(Angle, 1.0);
+UpdateModelview;
+
+glBindTexture(GL_TEXTURE_2D, Texture^.atlas^.id);
 
-
-glBindTexture(GL_TEXTURE_2D, Texture^.id);
+_l:= -hw + Texture^.cropInfo.l;
+_t:= -hh + Texture^.cropInfo.t;
+_r:= hw - Texture^.cropInfo.l - Texture^.cropInfo.r;
+_b:= hh - Texture^.cropInfo.t - Texture^.cropInfo.b;
 
-VertexBuffer[0].X:= -hw;
-VertexBuffer[0].Y:= -hh;
-VertexBuffer[1].X:= hw;
-VertexBuffer[1].Y:= -hh;
-VertexBuffer[2].X:= hw;
-VertexBuffer[2].Y:= hh;
-VertexBuffer[3].X:= -hw;
-VertexBuffer[3].Y:= hh;
+VertexBuffer[0].X:= _l;
+VertexBuffer[0].Y:= _t;
+VertexBuffer[1].X:= _r;
+VertexBuffer[1].Y:= _t;
+VertexBuffer[2].X:= _r;
+VertexBuffer[2].Y:= _b;
+VertexBuffer[3].X:= _l;
+VertexBuffer[3].Y:= _b;
 
-glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]);
-glTexCoordPointer(2, GL_FLOAT, 0, @Texture^.tb);
+SetVertexPointer(@VertexBuffer[0]);
+SetTexCoordPointer(@Texture^.tb);
 glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer));
 
-glPopMatrix
+ResetModelview;
 end;
 
 procedure DrawSprite(Sprite: TSprite; X, Y, Frame: LongInt);
-var row, col, numFramesFirstCol: LongInt;
+var 
+    r: TSDL_Rect;
+    tex: PTexture;
 begin
     if SpritesData[Sprite].imageHeight = 0 then
         exit;
-    numFramesFirstCol:= SpritesData[Sprite].imageHeight div SpritesData[Sprite].Height;
-    row:= Frame mod numFramesFirstCol;
-    col:= Frame div numFramesFirstCol;
-    DrawSprite(Sprite, X, Y, col, row);
-end;
+
+    tex:= SpritesData[Sprite].Texture;
+
+    while (Frame > 0) and (tex <> nil) do
+    begin
+        tex:= tex^.nextFrame;
+        dec(Frame);
+    end;
 
-procedure DrawSprite(Sprite: TSprite; X, Y, FrameX, FrameY: LongInt);
-var r: TSDL_Rect;
-begin
-    r.x:= FrameX * SpritesData[Sprite].Width;
+    if (tex = nil) or (tex^.w = 0) or (tex^.h = 0) then
+        exit;
+
+    r.x:= 0;
     r.w:= SpritesData[Sprite].Width;
-    r.y:= FrameY * SpritesData[Sprite].Height;
+    r.y:= 0;
     r.h:= SpritesData[Sprite].Height;
-    DrawTextureFromRect(X, Y, @r, SpritesData[Sprite].Texture)
+    DrawTextureFromRect(X, Y, @r, tex)
 end;
 
 procedure DrawSpriteClipped(Sprite: TSprite; X, Y, TopY, RightX, BottomY, LeftX: LongInt);
@@ -329,8 +401,9 @@
     glDisable(GL_TEXTURE_2D);
     glEnable(GL_LINE_SMOOTH);
 
-    glPushMatrix;
-    glTranslatef(WorldDx, WorldDy, 0);
+    ResetRotation;
+    SetOffset(WorldDx, WorldDy);
+    UpdateModelview;
     glLineWidth(Width);
 
     Tint(r, g, b, a);
@@ -339,11 +412,11 @@
     VertexBuffer[1].X:= X1;
     VertexBuffer[1].Y:= Y1;
 
-    glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]);
+    SetVertexPointer(@VertexBuffer[0]);
     glDrawArrays(GL_LINES, 0, Length(VertexBuffer));
     Tint($FF, $FF, $FF, $FF);
     
-    glPopMatrix;
+    ResetModelview;
     
     glEnable(GL_TEXTURE_2D);
     glDisable(GL_LINE_SMOOTH);
@@ -352,6 +425,10 @@
 procedure DrawFillRect(r: TSDL_Rect);
 var VertexBuffer: array [0..3] of TVertex2f;
 begin
+SetOffset(0, 0);
+ResetRotation;
+UpdateModelview;
+
 // do not draw anything outside the visible screen space (first check fixes some sprite drawing, e.g. hedgehogs)
 if (abs(r.x) > r.w) and ((abs(r.x + r.w / 2) - r.w / 2) * cScaleFactor > cScreenWidth) then
     exit;
@@ -371,11 +448,13 @@
 VertexBuffer[3].X:= r.x;
 VertexBuffer[3].Y:= r.y + r.h;
 
-glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]);
+SetVertexPointer(@VertexBuffer[0]);
 glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer));
 
 Tint($FF, $FF, $FF, $FF);
-glEnable(GL_TEXTURE_2D)
+glEnable(GL_TEXTURE_2D);
+
+ResetModelview;
 end;
 
 procedure DrawCircle(X, Y, Radius, Width: LongInt; r, g, b, a: Byte); 
@@ -396,23 +475,29 @@
     end;
     glDisable(GL_TEXTURE_2D);
     glEnable(GL_LINE_SMOOTH);
-    glPushMatrix;
+    SetOffset(0, 0);
+    ResetRotation;
+    UpdateModelview;
     glLineWidth(Width);
-    glVertexPointer(2, GL_FLOAT, 0, @CircleVertex[0]);
+    SetVertexPointer(@CircleVertex[0]);
     glDrawArrays(GL_LINE_LOOP, 0, 60);
-    glPopMatrix;
     glEnable(GL_TEXTURE_2D);
     glDisable(GL_LINE_SMOOTH);
+    ResetModelview;
 end;
 
 
 procedure DrawHedgehog(X, Y: LongInt; Dir: LongInt; Pos, Step: LongWord; Angle: real);
-const VertexBuffer: array [0..3] of TVertex2f = (
-        (X: -16; Y: -16),
-        (X:  16; Y: -16),
-        (X:  16; Y:  16),
-        (X: -16; Y:  16));
-var l, r, t, b: real;
+const VertexBuffers: array[0..1] of TVertexRect = (
+        ((x: -16; y: -16),
+         (x:  16; y: -16),
+         (x:  16; y:  16),
+         (x: -16; y:  16)),
+        ((x:  16; y: -16),
+         (x: -16; y: -16),
+         (x: -16; y:  16),
+         (x:  16; y:  16)));
+var r: TSDL_Rect;
     TextureBuffer: array [0..3] of TVertex2f;
 begin
     // do not draw anything outside the visible screen space (first check fixes some sprite drawing, e.g. hedgehogs)
@@ -421,41 +506,25 @@
     if (abs(Y) > 32) and ((abs(Y - 0.5 * cScreenHeight) - 16) * cScaleFactor > cScreenHeight) then
         exit;
 
-    t:= Pos * 32 / HHTexture^.h;
-    b:= (Pos + 1) * 32 / HHTexture^.h;
+    r.x:=Step * 32;
+    r.y:=Pos * 32;
+    r.w:=32;
+    r.h:=32;
+    ComputeTexcoords(HHTexture, @r, @TextureBuffer);
 
-    if Dir = -1 then
-        begin
-        l:= (Step + 1) * 32 / HHTexture^.w;
-        r:= Step * 32 / HHTexture^.w
-        end
-    else
-        begin
-        l:= Step * 32 / HHTexture^.w;
-        r:= (Step + 1) * 32 / HHTexture^.w
-    end;
-
+    SetOffset(X, Y);
+    SetRotation(Angle, 1.0);
+    UpdateModelview;
 
-    glPushMatrix();
-    glTranslatef(X, Y, 0);
-    glRotatef(Angle, 0, 0, 1);
-
-    glBindTexture(GL_TEXTURE_2D, HHTexture^.id);
+    glBindTexture(GL_TEXTURE_2D, HHTexture^.atlas^.id);
+    if Dir = -1 then
+        SetVertexPointer(@VertexBuffers[1][0])
+    else
+        SetVertexPointer(@VertexBuffers[0][0]);
+    SetTexCoordPointer(@TextureBuffer[0]);
+    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
 
-    TextureBuffer[0].X:= l;
-    TextureBuffer[0].Y:= t;
-    TextureBuffer[1].X:= r;
-    TextureBuffer[1].Y:= t;
-    TextureBuffer[2].X:= r;
-    TextureBuffer[2].Y:= b;
-    TextureBuffer[3].X:= l;
-    TextureBuffer[3].Y:= b;
-
-    glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]);
-    glTexCoordPointer(2, GL_FLOAT, 0, @TextureBuffer[0]);
-    glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer));
-
-    glPopMatrix
+    ResetModelview;
 end;
 
 procedure DrawScreenWidget(widget: POnScreenWidget);
@@ -505,31 +574,4 @@
 {$ENDIF}
 end;
 
-procedure Tint(r, g, b, a: Byte); inline;
-var nc, tw: Longword;
-begin
-    nc:= (a shl 24) or (b shl 16) or (g shl 8) or r;
-
-    if nc = lastTint then
-        exit;
-
-    if GrayScale then
-        begin
-        tw:= round(r * RGB_LUMINANCE_RED + g * RGB_LUMINANCE_GREEN + b * RGB_LUMINANCE_BLUE);
-        if tw > 255 then
-            tw:= 255;
-        r:= tw;
-        g:= tw;
-        b:= tw
-        end;
-
-    glColor4ub(r, g, b, a);
-    lastTint:= nc;
-end;
-
-procedure Tint(c: Longword); inline;
-begin
-    Tint(((c shr 24) and $FF), ((c shr 16) and $FF), (c shr 8) and $FF, (c and $FF))
-end;
-
 end.
--- a/hedgewars/uRenderUtils.pas	Mon Jul 09 23:28:02 2012 -0400
+++ b/hedgewars/uRenderUtils.pas	Tue Jul 10 11:09:38 2012 +0200
@@ -272,9 +272,7 @@
 
     TryDo(SDL_SetColorKey(finalSurface, SDL_SRCCOLORKEY, 0) = 0, errmsgTransparentSet, true);
 
-    RenderStringTexLim:= Surface2Tex(finalSurface, false);
-
-    SDL_FreeSurface(finalSurface);
+    RenderStringTexLim:= Surface2Atlas(finalSurface, false);
 end;
 
 
@@ -459,10 +457,9 @@
         inc(pos);
         end;
 
-    RenderSpeechBubbleTex:= Surface2Tex(finalSurface, true);
+    RenderSpeechBubbleTex:= Surface2Atlas(finalSurface, true);
 
     SDL_FreeSurface(rotatedEdge);
-    SDL_FreeSurface(finalSurface);
 end;
 
 end.
--- a/hedgewars/uScript.pas	Mon Jul 09 23:28:02 2012 -0400
+++ b/hedgewars/uScript.pas	Tue Jul 10 11:09:38 2012 +0200
@@ -750,8 +750,7 @@
             DrawRoundRect(@rr, clan^.Color, clan^.Color, texsurf, false);
 
             FreeTexture(team^.HealthTex);
-            team^.HealthTex:= Surface2Tex(texsurf, false);
-            SDL_FreeSurface(texsurf);
+            team^.HealthTex:= Surface2Atlas(texsurf, false);
             MakeCrossHairs
             end
         end;
--- a/hedgewars/uStore.pas	Mon Jul 09 23:28:02 2012 -0400
+++ b/hedgewars/uStore.pas	Tue Jul 10 11:09:38 2012 +0200
@@ -43,10 +43,23 @@
 
 procedure WarpMouse(x, y: Word); inline;
 procedure SwapBuffers; inline;
+procedure UpdateProjection;
+
+{$IFDEF GL2}
+procedure UpdateModelviewProjection;
+{$ENDIF}
+
+procedure Tint(r, g, b, a: Byte); inline;
+procedure Tint(c: Longword); inline;
+procedure SetTexCoordPointer(p: Pointer);
+procedure SetVertexPointer(p: Pointer);
+procedure SetColorPointer(p: Pointer);
+procedure BeginWater;
+procedure EndWater;
 
 implementation
 uses uMisc, uConsole, uMobile, uVariables, uUtils, uTextures, uRender, uRenderUtils, uCommands,
-     uDebug{$IFDEF USE_CONTEXT_RESTORE}, uWorld{$ENDIF};
+     uDebug{$IFDEF USE_CONTEXT_RESTORE}, uWorld{$ENDIF}, uMatrix, uAtlas;
 
 //type TGPUVendor = (gvUnknown, gvNVIDIA, gvATI, gvIntel, gvApple);
 
@@ -57,6 +70,27 @@
 {$ELSE}
     SDLPrimSurface: PSDL_Surface;
 {$ENDIF}
+{$IFDEF GL2}
+    shaderMain: GLuint;
+    shaderWater: GLuint;
+
+    // attributes
+const
+    aVertex: GLint   = 0;
+    aTexCoord: GLint = 1;
+    aColor: GLint    = 2;
+
+var
+    uCurrentMVPLocation: GLint;
+
+    uMainMVPLocation: GLint;
+    uMainTintLocation: GLint;
+
+    uWaterMVPLocation: GLint;
+
+{$ENDIF}
+    LastTint: LongWord = 0;
+
 
 function WriteInRect(Surface: PSDL_Surface; X, Y: LongInt; Color: LongWord; Font: THWFont; s: ansistring): TSDL_Rect;
 var w, h: LongInt;
@@ -121,8 +155,7 @@
         SDL_UnlockSurface(texsurf);
 
     FreeTexture(CrosshairTex);
-    CrosshairTex:= Surface2Tex(texsurf, false);
-    SDL_FreeSurface(texsurf)
+    CrosshairTex:= Surface2Atlas(texsurf, false);
     end;
 
 SDL_FreeSurface(tmpsurf)
@@ -155,8 +188,7 @@
         rr:= r;
         inc(rr.x, 2); dec(rr.w, 4); inc(rr.y, 2); dec(rr.h, 4);
         DrawRoundRect(@rr, Clan^.Color, Clan^.Color, texsurf, false);
-        HealthTex:= Surface2Tex(texsurf, false);
-        SDL_FreeSurface(texsurf);
+        HealthTex:= Surface2Atlas(texsurf, false);
 
         r.x:= 0;
         r.y:= 0;
@@ -196,8 +228,7 @@
         PLongwordArray(texsurf^.pixels)^[32 * 16 +  2]:= cNearBlackColor;
         PLongwordArray(texsurf^.pixels)^[32 * 16 + 23]:= cNearBlackColor;
 
-        FlagTex:= Surface2Tex(texsurf, false);
-        SDL_FreeSurface(texsurf);
+        FlagTex:= Surface2Atlas(texsurf, false);
         texsurf:= nil;
 
         AIKillsTex := RenderStringTex(inttostr(stats.AIKills), Clan^.Color, fnt16);
@@ -229,8 +260,7 @@
         r.w:= 28;
         r.h:= 28;
         DrawRoundRect(@r, cWhiteColor, cNearBlackColor, iconsurf, true);
-        ropeIconTex:= Surface2Tex(iconsurf, false);
-        SDL_FreeSurface(iconsurf);
+        ropeIconTex:= Surface2Atlas(iconsurf, false);
         iconsurf:= nil;
         end;
 end;
@@ -265,8 +295,7 @@
                 texsurf:= LoadImage(UserPathz[ptGraves] + '/Statue', ifTransparent);
             if texsurf = nil then
                 texsurf:= LoadImage(Pathz[ptGraves] + '/Statue', ifCritical or ifTransparent);
-            GraveTex:= Surface2Tex(texsurf, false);
-            SDL_FreeSurface(texsurf)
+            GraveTex:= SurfaceSheet2Atlas(texsurf, 32, 32);
             end
 end;
 
@@ -277,6 +306,7 @@
     ai: TAmmoType;
     tmpsurf: PSDL_Surface;
     i: LongInt;
+    sw, sh: LongInt;
 begin
 AddFileLog('StoreLoad()');
 
@@ -363,12 +393,29 @@
                     end;
                 if (ii in [sprSky, sprSkyL, sprSkyR, sprHorizont, sprHorizontL, sprHorizontR]) then
                     begin
-                    Texture:= Surface2Tex(tmpsurf, true);
+                    Texture:= Surface2Atlas(tmpsurf, true);
                     Texture^.Scale:= 2
                     end
                 else
                     begin
-                    Texture:= Surface2Tex(tmpsurf, false);
+                    sw:=Trunc(Width*scale);
+                    sh:=Trunc(Height*scale);
+                    if (sw > imageWidth) or (sh > imageHeight) then
+                    begin
+                        if not (ii in [sprPHammer, sprBalls, sprSnow]) then
+                        begin
+                            writeln(stdout, 'Dimension error in ' + FileName + ' [' + IntToStr(Integer(ii)) + ']');
+                            halt(-1);
+                        end;
+                    end;
+ 
+                    if (imageWidth > sw) or (imageHeight > sh) then
+                    begin
+                        writeln(stdout, 'Animation sheet?: ' + FileName + ' : ' + IntToStr(Round(imageWidth/sw)) + 'x' + IntToStr(Round(imageHeight/sh)));
+                        Texture:= SurfaceSheet2Atlas(tmpsurf, Width, Height);
+                    end
+                    else Texture:= Surface2Atlas(tmpsurf, false);
+
                     // HACK: We should include some sprite attribute to define the texture wrap directions
                     if ((ii = sprWater) or (ii = sprSDWater)) and ((cReducedQuality and (rq2DWater or rqClampLess)) = 0) then
                         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
@@ -382,8 +429,8 @@
 {$ELSE}
                     if saveSurf then
                         Surface:= tmpsurf
-                    else
-                        SDL_FreeSurface(tmpsurf)
+                    //else
+                    //    SDL_FreeSurface(tmpsurf) released by FreeTexture
 {$ENDIF}
                     end
                 end
@@ -398,8 +445,7 @@
 if tmpsurf = nil then
     tmpsurf:= LoadImage(Pathz[ptGraphics] + '/' + cHHFileName, ifAlpha or ifCritical or ifTransparent);
     
-HHTexture:= Surface2Tex(tmpsurf, false);
-SDL_FreeSurface(tmpsurf);
+HHTexture:= SurfaceSheet2Atlas(tmpsurf, 32, 32);
 
 InitHealth;
 
@@ -419,8 +465,7 @@
         TryDo(tmpsurf <> nil,'Name-texture creation for ammo type #' + intToStr(ord(ai)) + ' failed!',true);
         tmpsurf:= doSurfaceConversion(tmpsurf);
         FreeTexture(NameTex);
-        NameTex:= Surface2Tex(tmpsurf, false);
-        SDL_FreeSurface(tmpsurf)
+        NameTex:= Surface2Atlas(tmpsurf, false);
         end;
 
 // number of weapons in ammo menu
@@ -429,8 +474,7 @@
     tmpsurf:= TTF_RenderUTF8_Blended(Fontz[fnt16].Handle, Str2PChar(IntToStr(i) + 'x'), cWhiteColorChannels);
     tmpsurf:= doSurfaceConversion(tmpsurf);
     FreeTexture(CountTexz[i]);
-    CountTexz[i]:= Surface2Tex(tmpsurf, false);
-    SDL_FreeSurface(tmpsurf)
+    CountTexz[i]:= Surface2Atlas(tmpsurf, false);
     end;
 
 if not reload then
@@ -442,16 +486,14 @@
 var ii: TSprite;
     ai: TAmmoType;
     i, t: LongInt;
+    nf: ^PTexture;
 begin
 for ii:= Low(TSprite) to High(TSprite) do
     begin
+    nf:= @SpritesData[ii].Texture;
+    writeln(stdout, 'Releasing ' + IntToStr(Integer(ii)) + ' tex: ' + IntToHex(Integer(nf), 8));
     FreeTexture(SpritesData[ii].Texture);
     SpritesData[ii].Texture:= nil;
-    if (SpritesData[ii].Surface <> nil) and (not reload) then
-        begin
-        SDL_FreeSurface(SpritesData[ii].Surface);
-        SpritesData[ii].Surface:= nil
-        end
     end;
 SDL_FreeSurface(MissionIcons);
 
@@ -582,10 +624,7 @@
         FreeTexture(HHGear^.Hedgehog^.HatTex);
 
         // assign new hat to hedgehog
-        HHGear^.Hedgehog^.HatTex:= Surface2Tex(texsurf, true);
-
-        // cleanup: free temporary surface mem
-        SDL_FreeSurface(texsurf)
+        HHGear^.Hedgehog^.HatTex:= SurfaceSheet2Atlas(texsurf, 32, 32)
         end;
 end;
 
@@ -625,6 +664,112 @@
     SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); // try to prefer hardware rendering
 end;
 
+{$IFDEF GL2}
+function CompileShader(shaderFile: string; shaderType: GLenum): GLuint;
+var
+    shader: GLuint;
+    f: Textfile;
+    source, line: AnsiString;
+    sourceA: Pchar;
+    lengthA: GLint;
+    compileResult: GLint;
+    logLength: GLint;
+    log: PChar;
+begin
+    Assign(f, Pathz[ptShaders] + '/' + shaderFile);
+    filemode:= 0; // readonly
+    Reset(f);
+    if IOResult <> 0 then
+    begin
+        AddFileLog('Unable to load ' + shaderFile);
+        halt(-1);
+    end;
+
+    source:='';
+    while not eof(f) do
+    begin
+        ReadLn(f, line);
+        source:= source + line + #10;
+    end;
+    
+    CloseFile(f);
+
+    WriteLnToConsole('Compiling shader: ' + Pathz[ptShaders] + '/' + shaderFile);
+
+    sourceA:=PChar(source);
+    lengthA:=Length(source);
+
+    shader:=glCreateShader(shaderType);
+    glShaderSource(shader, 1, @sourceA, @lengthA);
+    glCompileShader(shader);
+    glGetShaderiv(shader, GL_COMPILE_STATUS, @compileResult);
+    glGetShaderiv(shader, GL_INFO_LOG_LENGTH, @logLength);
+
+    if logLength > 1 then
+    begin
+        GetMem(log, logLength);
+        glGetShaderInfoLog(shader, logLength, nil, log);
+        WriteLnToConsole('========== Compiler log  ==========');
+        WriteLnToConsole(log);
+        WriteLnToConsole('===================================');
+        FreeMem(log, logLength);
+    end;
+
+    if compileResult <> GL_TRUE then
+    begin
+        WriteLnToConsole('Shader compilation failed, halting');
+        halt(-1);
+    end;
+
+    CompileShader:= shader;
+end;
+
+function CompileProgram(shaderName: string): GLuint;
+var
+    program_: GLuint;
+    vs, fs: GLuint;
+    linkResult: GLint;
+    logLength: GLint;
+    log: PChar;
+begin
+    program_:= glCreateProgram();
+    vs:= CompileShader(shaderName + '.vs', GL_VERTEX_SHADER);
+    fs:= CompileShader(shaderName + '.fs', GL_FRAGMENT_SHADER);
+    glAttachShader(program_, vs);
+    glAttachShader(program_, fs);
+
+    glBindAttribLocation(program_, aVertex, 'vertex');
+    glBindAttribLocation(program_, aTexCoord, 'texcoord');
+    glBindAttribLocation(program_, aColor, 'color');
+
+    glLinkProgram(program_);
+    glDeleteShader(vs);
+    glDeleteShader(fs);
+
+    glGetProgramiv(program_, GL_LINK_STATUS, @linkResult);
+    glGetProgramiv(program_, GL_INFO_LOG_LENGTH, @logLength);
+
+    if logLength > 1 then
+    begin
+        GetMem(log, logLength);
+        glGetProgramInfoLog(program_, logLength, nil, log);
+        WriteLnToConsole('========== Compiler log  ==========');
+        WriteLnToConsole(log);
+        WriteLnToConsole('===================================');
+        FreeMem(log, logLength);
+    end;
+
+    if linkResult <> GL_TRUE then
+    begin
+        WriteLnToConsole('Linking program failed, halting');
+        halt(-1);
+    end;
+
+    CompileProgram:= program_;
+end;
+
+{$ENDIF}
+
 procedure SetupOpenGL;
 //var vendor: shortstring = '';
 var buf: array[byte] of char;
@@ -682,6 +827,26 @@
     AddFileLog('  \----- Extensions: ' + shortstring(pchar(glGetString(GL_EXTENSIONS))));
     //TODO: don't have the Extensions line trimmed but slipt it into multiple lines
 
+{$IFDEF GL2}
+    Load_GL_VERSION_2_0;
+
+    shaderWater:= CompileProgram('water');
+    glUseProgram(shaderWater);
+    glUniform1i(glGetUniformLocation(shaderWater, 'tex0'), 0);
+    uWaterMVPLocation:= glGetUniformLocation(shaderWater, 'mvp');
+
+    shaderMain:= CompileProgram('default');
+    glUseProgram(shaderMain);
+    glUniform1i(glGetUniformLocation(shaderMain, 'tex0'), 0);
+    uMainMVPLocation:= glGetUniformLocation(shaderMain, 'mvp');
+    uMainTintLocation:= glGetUniformLocation(shaderMain, 'tint');
+
+    uCurrentMVPLocation:= uMainMVPLocation;
+
+    Tint(255, 255, 255, 255);
+    UpdateModelviewProjection;
+{$ENDIF}
+
 {$IFNDEF S3D_DISABLED}
     if (cStereoMode = smHorizontal) or (cStereoMode = smVertical) or (cStereoMode = smAFR) then
     begin
@@ -729,9 +894,7 @@
 
     glMatrixMode(GL_MODELVIEW);
     // prepare default translation/scaling
-    glLoadIdentity();
-    glScalef(2.0 / cScreenWidth, -2.0 / cScreenHeight, 1.0);
-    glTranslatef(0, -cScreenHeight / 2, 0);
+    SetScale(2.0);
 
     // enable alpha blending
     glEnable(GL_BLEND);
@@ -742,27 +905,144 @@
     glDisable(GL_DITHER);
     // enable common states by default as they save a lot
     glEnable(GL_TEXTURE_2D);
+
+{$IFDEF GL2}
+    glEnableVertexAttribArray(aVertex);
+    glEnableVertexAttribArray(aTexCoord);
+{$ELSE}
     glEnableClientState(GL_VERTEX_ARRAY);
     glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+{$ENDIF}
+end;
+
+
+procedure Tint(r, g, b, a: Byte); inline;
+var
+    nc, tw: Longword;
+const
+    scale = 1.0/255.0;
+begin
+    nc:= (a shl 24) or (b shl 16) or (g shl 8) or r;
+
+    if nc = lastTint then
+        exit;
+
+    if GrayScale then
+        begin
+        tw:= round(r * RGB_LUMINANCE_RED + g * RGB_LUMINANCE_GREEN + b * RGB_LUMINANCE_BLUE);
+        if tw > 255 then
+            tw:= 255;
+        r:= tw;
+        g:= tw;
+        b:= tw
+        end;
+
+    {$IFDEF GL2}
+    glUniform4f(uMainTintLocation, r*scale, g*scale, b*scale, a*scale);
+    glColor4ub(r, g, b, a);
+    {$ELSE}
+    glColor4ub(r, g, b, a);
+    {$ENDIF}
+    lastTint:= nc;
+end;
+
+procedure Tint(c: Longword); inline;
+begin
+    Tint(((c shr 24) and $FF), ((c shr 16) and $FF), (c shr 8) and $FF, (c and $FF))
+end;
+
+procedure SetTexCoordPointer(p: Pointer);
+begin
+    {$IFDEF GL2}
+    glVertexAttribPointer(aTexCoord, 2, GL_FLOAT, GL_FALSE, 0, p);
+    {$ELSE}
+    glTexCoordPointer(2, GL_FLOAT, 0, p);
+    {$ENDIF}
+end;
+
+procedure SetVertexPointer(p: Pointer);
+begin
+    {$IFDEF GL2}
+    glVertexAttribPointer(aVertex, 2, GL_FLOAT, GL_FALSE, 0, p);
+    {$ELSE}
+    glVertexPointer(2, GL_FLOAT, 0, p);
+    {$ENDIF}
+end;
+
+procedure SetColorPointer(p: Pointer);
+begin
+    {$IFDEF GL2}
+    glVertexAttribPointer(aColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, p);
+    {$ELSE}
+    glColorPointer(4, GL_UNSIGNED_BYTE, 0, p);
+    {$ENDIF}
+end;
+
+{$IFDEF GL2}
+procedure UpdateModelviewProjection;
+var
+    mvp: TMatrix4x4f;
+begin
+    MatrixMultiply(mvp, mProjection, mModelview);
+    glUniformMatrix4fv(uCurrentMVPLocation, 1, GL_FALSE, @mvp[0, 0]);
+end;
+{$ENDIF GL2}
+
+procedure UpdateProjection;
+var
+    s: GLfloat;
+begin
+    s:=cScaleFactor;
+    mProjection[0,0]:= s/cScreenWidth; mProjection[0,1]:=  0.0;             mProjection[0,2]:=0.0; mProjection[0,3]:=  0.0;
+    mProjection[1,0]:= 0.0;            mProjection[1,1]:= -s/cScreenHeight; mProjection[1,2]:=0.0; mProjection[1,3]:=  0.0;
+    mProjection[2,0]:= 0.0;            mProjection[2,1]:=  0.0;             mProjection[2,2]:=1.0; mProjection[2,3]:=  0.0;
+    mProjection[3,0]:= cStereoDepth;   mProjection[3,1]:=  s/2;             mProjection[3,2]:=0.0; mProjection[3,3]:=  1.0;
+
+    {$IFDEF GL2}
+    UpdateModelviewProjection;
+    {$ELSE}
+    glMatrixMode(GL_PROJECTION);
+    glLoadMatrixf(@mProjection[0, 0]);
+    glMatrixMode(GL_MODELVIEW);
+    {$ENDIF}    
 end;
 
 procedure SetScale(f: GLfloat);
 begin
-// leave immediately if scale factor did not change
-    if f = cScaleFactor then
-        exit;
+    // This lazy update conflicts with R7103 at the moment, missing the initial SetScale(2.0)
+    //if cScaleFactor <> f then
+    //begin
+        cScaleFactor:=f;
+        UpdateProjection;
+    //end;
+end;
 
-    if f = cDefaultZoomLevel then
-        glPopMatrix         // "return" to default scaling
-    else                    // other scaling
-        begin
-        glPushMatrix;       // save default scaling
-        glLoadIdentity;
-        glScalef(f / cScreenWidth, -f / cScreenHeight, 1.0);
-        glTranslatef(0, -cScreenHeight / 2, 0);
-        end;
+procedure BeginWater;
+begin
+    {$IFDEF GL2}
+    glUseProgram(shaderWater);
+    uCurrentMVPLocation:=uWaterMVPLocation;
+    UpdateModelviewProjection;
+    glDisableVertexAttribArray(aTexCoord);
+    glEnableVertexAttribArray(aColor);
+    {$ELSE}
+    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+    glEnableClientState(GL_COLOR_ARRAY);
+    {$ENDIF}
+end;
 
-    cScaleFactor:= f;
+procedure EndWater;
+begin
+    {$IFDEF GL2}
+    glUseProgram(shaderMain);
+    uCurrentMVPLocation:=uMainMVPLocation;
+    UpdateModelviewProjection;
+    glDisableVertexAttribArray(aColor);
+    glEnableVertexAttribArray(aTexCoord);
+    {$ELSE}
+    glDisableClientState(GL_COLOR_ARRAY);
+    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+    {$ENDIF}
 end;
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -777,11 +1057,10 @@
         if texsurf = nil then
             texsurf:= LoadImage(Pathz[ptGraphics] + '/Progress', ifCritical or ifTransparent);
 
-        ProgrTex:= Surface2Tex(texsurf, false);
-
         squaresize:= texsurf^.w shr 1;
         numsquares:= texsurf^.h div squaresize;
-        SDL_FreeSurface(texsurf);
+
+        ProgrTex:= Surface2Atlas(texsurf, false);
 
         uMobile.GameLoading();
         end;
@@ -926,8 +1205,7 @@
 SDL_FillRect(tmpsurf, @r, $ffffffff);
 SDL_UpperBlit(iconsurf, iconrect, tmpsurf, @r);
 
-RenderHelpWindow:=  Surface2Tex(tmpsurf, true);
-SDL_FreeSurface(tmpsurf)
+RenderHelpWindow:=  Surface2Atlas(tmpsurf, true);
 end;
 
 procedure RenderWeaponTooltip(atype: TAmmoType);
@@ -1033,11 +1311,7 @@
         // chFullScr is called when there is a rotation event and needs the SetScale and SetupOpenGL to set up the new resolution
         // this 6 gl functions are the relevant ones and are hacked together here for optimisation
         glMatrixMode(GL_MODELVIEW);
-        glPopMatrix;
-        glLoadIdentity();
-        glScalef(2.0 / cScreenWidth, -2.0 / cScreenHeight, 1.0);
-        glTranslatef(0, -cScreenHeight / 2, 0);
-        glViewport(0, 0, cScreenWidth, cScreenHeight);
+        SetScale(2.0);
         exit;
 {$ELSE}
         SetScale(cDefaultZoomLevel);
@@ -1151,6 +1425,10 @@
 
 procedure freeModule;
 begin
+{$IFDEF GL2}
+    glDeleteProgram(shaderMain);
+    glDeleteProgram(shaderWater);
+{$ENDIF}
     StoreRelease(false);
     TTF_Quit();
 {$IFDEF SDL13}
--- a/hedgewars/uTextures.pas	Mon Jul 09 23:28:02 2012 -0400
+++ b/hedgewars/uTextures.pas	Tue Jul 10 11:09:38 2012 +0200
@@ -24,17 +24,173 @@
 
 function  NewTexture(width, height: Longword; buf: Pointer): PTexture;
 procedure Surface2GrayScale(surf: PSDL_Surface);
-function  Surface2Tex(surf: PSDL_Surface; enableClamp: boolean): PTexture;
+function SurfaceSheet2Atlas(surf: PSDL_Surface; spriteWidth: Integer; spriteHeight: Integer): PTexture;
+function  Surface2Atlas(surf: PSDL_Surface; enableClamp: boolean): PTexture;
 procedure FreeTexture(tex: PTexture);
+procedure ComputeTexcoords(texture: PTexture; r: PSDL_Rect; tb: PVertexRect);
 
 procedure initModule;
 procedure freeModule;
 
 implementation
-uses GLunit, uUtils, uVariables, uConsts, uDebug, uConsole;
+uses GLunit, uUtils, uVariables, uConsts, uDebug, uConsole, uAtlas, SysUtils;
+
+var
+  logFile: TextFile;
+
+function CropSurface(source: PSDL_Surface; rect: PSDL_Rect): PSDL_Surface;
+var
+    fmt: PSDL_PixelFormat;
+    srcP, dstP: PByte;
+    copySize: Integer;
+    i: Integer;
+const
+    pixelSize = 4;
+begin
+    //writeln(stdout, 'Cropping from ' + IntToStr(source^.w) + 'x' + IntToStr(source^.h) + ' -> ' + IntToStr(rect^.w) + 'x' + IntToStr(rect^.h));
+
+    fmt:= source^.format;
+
+    CropSurface:= SDL_CreateRGBSurface(source^.flags, rect^.w, rect^.h, 
+        fmt^.BitsPerPixel, fmt^.Rmask, fmt^.Gmask, fmt^.Bmask, fmt^.Amask);
+
+    if SDL_MustLock(source) then
+        SDLTry(SDL_LockSurface(source) >= 0, true);
+    if SDL_MustLock(CropSurface) then
+        SDLTry(SDL_LockSurface(CropSurface) >= 0, true);
+
+    srcP:= source^.pixels;
+    dstP:= CropSurface^.pixels;
+
+    inc(srcP, pixelSize * rect^.x);
+    inc(srcP, source^.pitch * rect^.y);
+    copySize:= rect^.w * pixelSize;
+    for i:= 0 to Pred(rect^.h) do
+    begin
+        Move(srcP^, dstP^, copySize);
+        inc(srcP, source^.pitch);
+        inc(dstP, CropSurface^.pitch);
+    end;
+
+    if SDL_MustLock(source) then
+        SDL_UnlockSurface(source);
+    if SDL_MustLock(CropSurface) then
+        SDL_UnlockSurface(CropSurface);
+end;
+
+function TransparentLine(p: PByte; stride: Integer; length: Integer): boolean;
+var
+    i: Integer;
+begin
+    TransparentLine:= false;
+    for i:=0 to pred(length) do
+    begin
+        if p^ <> 0 then
+            exit;
+        inc(p, stride);
+    end;
+    TransparentLine:= true;
+end;
+
+function AutoCrop(source: PSDL_Surface; var cropinfo: TCropInformation): PSDL_Surface;
+var
+    l,r,t,b, i: Integer;
+    pixels, p: PByte;
+    scanlineSize: Integer;
+    rect: TSDL_Rect;
+const
+    pixelSize = 4;
+begin
+    l:= source^.w; 
+    r:= 0; 
+    t:= source^.h;
+    b:= 0;
+
+    if SDL_MustLock(source) then
+        SDLTry(SDL_LockSurface(source) >= 0, true);
+
+    pixels:= source^.pixels;
+    scanlineSize:= source^.pitch;
 
-var TextureList: PTexture;
+    inc(pixels, 3); // advance to alpha value
+
+    // check top
+    p:= pixels;
+    for i:= 0 to Pred(source^.h) do
+    begin
+        if not TransparentLine(p, pixelSize, source^.w) then
+        begin
+            t:= i;
+            break;
+        end;
+        inc(p, scanlineSize);
+    end;
+
+
+    // check bottom
+    p:= pixels;
+    inc(p, scanlineSize * source^.h);
+    for i:= 0 to Pred(source^.h - t) do
+    begin
+        dec(p, scanlineSize);
+        if not TransparentLine(p, pixelSize, source^.w) then
+        begin
+            b:= i;
+            break;
+        end;
+    end;
+
+    // check left
+    p:= pixels;
+    for i:= 0 to Pred(source^.w) do
+    begin
+        if not TransparentLine(p, scanlineSize, source^.h) then
+        begin
+            l:= i;
+            break;
+        end;
+        inc(p, pixelSize);
+    end;
 
+    // check right
+    p:= pixels;
+    inc(p, scanlineSize);
+    for i:= 0 to Pred(source^.w - l) do
+    begin
+        dec(p, pixelSize);
+        if not TransparentLine(p, scanlineSize, source^.h) then
+        begin
+            r:= i;
+            break;
+        end;
+    end;
+
+    if SDL_MustLock(source) then
+        SDL_UnlockSurface(source);
+
+    rect.x:= l;
+    rect.y:= t;
+
+    rect.w:= source^.w - r - l;    
+    rect.h:= source^.h - b - t;
+
+    cropInfo.l:= l;
+    cropInfo.r:= r;
+    cropInfo.t:= t;
+    cropInfo.b:= b;
+    cropInfo.x:= Trunc(source^.w / 2 - l + r);
+    cropInfo.y:= Trunc(source^.h / 2 - t + b);
+
+    if (l = source^.w) or (t = source^.h) then
+    begin
+        result:= nil;
+        exit;
+    end;
+
+    if (l <> 0) or (r <> 0) or (t <> 0) or (b <> 0) then
+        result:= CropSurface(source, @rect)
+    else result:= source;
+end;
 
 procedure SetTextureParameters(enableClamp: Boolean);
 begin
@@ -47,53 +203,107 @@
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
 end;
 
-procedure ResetVertexArrays(texture: PTexture);
+procedure ComputeTexcoords(texture: PTexture; r: PSDL_Rect; tb: PVertexRect);
+var 
+    x0, y0, x1, y1, tmp: Real;
+    w, h, aw, ah: LongInt;
+    p: PChar;
+const 
+    texelOffset = 0.0;
 begin
-with texture^ do
+    aw:=texture^.atlas^.w;
+    ah:=texture^.atlas^.h;
+
+    if texture^.isRotated then
+    begin
+        w:=r^.h;
+        h:=r^.w;
+    end else
+    begin
+        w:=r^.w;
+        h:=r^.h;        
+    end;
+
+    x0:= (texture^.x + {r^.x} +     texelOffset)/aw;
+    x1:= (texture^.x + {r^.x} + w - texelOffset)/aw;
+    y0:= (texture^.y + {r^.y} +     texelOffset)/ah;
+    y1:= (texture^.y + {r^.y} + h - texelOffset)/ah;
+
+    if (texture^.isRotated) then
     begin
-    vb[0].X:= 0;
-    vb[0].Y:= 0;
-    vb[1].X:= w;
-    vb[1].Y:= 0;
-    vb[2].X:= w;
-    vb[2].Y:= h;
-    vb[3].X:= 0;
-    vb[3].Y:= h;
+        tb^[0].X:= x0;
+        tb^[0].Y:= y0;
+        tb^[3].X:= x1;
+        tb^[3].Y:= y0;
+        tb^[2].X:= x1;
+        tb^[2].Y:= y1;
+        tb^[1].X:= x0;
+        tb^[1].Y:= y1
+    end else
+    begin
+        tb^[0].X:= x0;
+        tb^[0].Y:= y0;
+        tb^[1].X:= x1;
+        tb^[1].Y:= y0;
+        tb^[2].X:= x1;
+        tb^[2].Y:= y1;
+        tb^[3].X:= x0;
+        tb^[3].Y:= y1;
+    end;
+end;
 
-    tb[0].X:= 0;
-    tb[0].Y:= 0;
-    tb[1].X:= rx;
-    tb[1].Y:= 0;
-    tb[2].X:= rx;
-    tb[2].Y:= ry;
-    tb[3].X:= 0;
-    tb[3].Y:= ry
+procedure ResetVertexArrays(texture: PTexture);
+var r: TSDL_Rect;
+begin
+    with texture^ do
+    begin
+        vb[0].X:= texture^.cropInfo.l;
+        vb[0].Y:= texture^.cropInfo.t;
+        vb[1].X:= texture^.cropInfo.l + w;
+        vb[1].Y:= texture^.cropInfo.t;
+        vb[2].X:= texture^.cropInfo.l + w;
+        vb[2].Y:= texture^.cropInfo.t + h;
+        vb[3].X:= texture^.cropInfo.l;
+        vb[3].Y:= texture^.cropInfo.t + h;
     end;
+
+    r.x:= 0;
+    r.y:= 0;
+    r.w:= texture^.w;
+    r.h:= texture^.h;
+    ComputeTexcoords(texture, @r, @texture^.tb);
 end;
 
 function NewTexture(width, height: Longword; buf: Pointer): PTexture;
 begin
 new(NewTexture);
-NewTexture^.PrevTexture:= nil;
-NewTexture^.NextTexture:= nil;
 NewTexture^.Scale:= 1;
-if TextureList <> nil then
-    begin
-    TextureList^.PrevTexture:= NewTexture;
-    NewTexture^.NextTexture:= TextureList
-    end;
-TextureList:= NewTexture;
 
-NewTexture^.w:= width;
-NewTexture^.h:= height;
-NewTexture^.rx:= 1.0;
-NewTexture^.ry:= 1.0;
+// Atlas allocation happens here later on. For now we just allocate one exclusive atlas per sprite
+new(NewTexture^.atlas);
+NewTexture^.atlas^.w:=width;
+NewTexture^.atlas^.h:=height;
+NewTexture^.x:=0;
+NewTexture^.y:=0;
+NewTexture^.w:=width;
+NewTexture^.h:=height;
+NewTexture^.isRotated:=false;
+NewTexture^.shared:=false;
+NewTexture^.surface:=nil;
+NewTexture^.nextFrame:=nil;
+NewTexture^.cropInfo.l:= 0;
+NewTexture^.cropInfo.r:= 0;
+NewTexture^.cropInfo.t:= 0;
+NewTexture^.cropInfo.b:= 0;
+NewTexture^.cropInfo.x:= width div 2;
+NewTexture^.cropInfo.y:= height div 2;
+
 
 ResetVertexArrays(NewTexture);
 
-glGenTextures(1, @NewTexture^.id);
+glGenTextures(1, @NewTexture^.atlas^.id);
 
-glBindTexture(GL_TEXTURE_2D, NewTexture^.id);
+glBindTexture(GL_TEXTURE_2D, NewTexture^.atlas^.id);
 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf);
 
 SetTextureParameters(true);
@@ -121,35 +331,112 @@
 end;
 
 
-function Surface2Tex(surf: PSDL_Surface; enableClamp: boolean): PTexture;
+function SurfaceSheet2Atlas(surf: PSDL_Surface; spriteWidth: Integer; spriteHeight: Integer): PTexture;
+var
+    subSurface: PSDL_Surface;
+    framesX, framesY: Integer;
+    last, current: PTexture;
+    r: TSDL_Rect;
+    x, y: Integer;
+begin
+    SurfaceSheet2Atlas:= nil;
+    r.x:= 0;
+    r.y:= 0;
+    r.w:= spriteWidth;
+    r.h:= spriteHeight;
+    last:= nil;
+
+    framesX:= surf^.w div spriteWidth;
+    framesY:= surf^.h div spriteHeight;
+
+    for x:=0 to Pred(framesX) do
+    begin
+        r.y:= 0;
+        for y:=0 to Pred(framesY) do
+        begin
+            subSurface:= CropSurface(surf, @r);
+            current:= Surface2Atlas(subSurface, false);
+
+            if last = nil then
+            begin
+                SurfaceSheet2Atlas:= current;
+                last:= current;
+            end else
+            begin
+                last^.nextFrame:= current;
+                last:= current;
+            end;
+            inc(r.y, spriteHeight);
+        end;
+        inc(r.x, spriteWidth);
+    end;
+
+    SDL_FreeSurface(surf);
+end;
+
+function Surface2Atlas(surf: PSDL_Surface; enableClamp: boolean): PTexture;
 var tw, th, x, y: Longword;
     tmpp: pointer;
+    cropped: PSDL_Surface;
     fromP4, toP4: PLongWordArray;
+    cropInfo: TCropInformation;
 begin
-new(Surface2Tex);
-Surface2Tex^.PrevTexture:= nil;
-Surface2Tex^.NextTexture:= nil;
-if TextureList <> nil then
+    cropped:= AutoCrop(surf, cropInfo);
+    if cropped <> surf then
+    begin
+        SDL_FreeSurface(surf);
+        surf:= cropped;
+    end;
+
+    if surf = nil then
     begin
-    TextureList^.PrevTexture:= Surface2Tex;
-    Surface2Tex^.NextTexture:= TextureList
+        new(Surface2Atlas);
+        Surface2Atlas^.w:= 0;
+        Surface2Atlas^.h:= 0;
+        Surface2Atlas^.x:=0 ;
+        Surface2Atlas^.y:=0 ;
+        Surface2Atlas^.isRotated:= false;
+        Surface2Atlas^.surface:= nil;
+        Surface2Atlas^.shared:= false;
+        Surface2Atlas^.nextFrame:= nil;
+        Surface2Atlas^.cropInfo:= cropInfo;
+        exit;
     end;
-TextureList:= Surface2Tex;
+
+    if (surf^.w <= 512) and (surf^.h <= 512) then
+    begin
+        Surface2Atlas:= Surface2Tex_(surf, enableClamp); // run the atlas side by side for debugging
+        Surface2Atlas^.cropInfo:= cropInfo;
+        ResetVertexArrays(Surface2Atlas);
+        exit;
+    end;
+new(Surface2Atlas);
 
-Surface2Tex^.w:= surf^.w;
-Surface2Tex^.h:= surf^.h;
+// Atlas allocation happens here later on. For now we just allocate one exclusive atlas per sprite
+new(Surface2Atlas^.atlas);
+
+Surface2Atlas^.w:= surf^.w;
+Surface2Atlas^.h:= surf^.h;
+Surface2Atlas^.x:=0;
+Surface2Atlas^.y:=0;
+Surface2Atlas^.isRotated:=false;
+Surface2Atlas^.surface:= surf;
+Surface2Atlas^.shared:= false;
+Surface2Atlas^.nextFrame:= nil;
+Surface2Atlas^.cropInfo:= cropInfo;
+
 
 if (surf^.format^.BytesPerPixel <> 4) then
     begin
     TryDo(false, 'Surface2Tex failed, expecting 32 bit surface', true);
-    Surface2Tex^.id:= 0;
-    exit
+    Surface2Atlas^.atlas^.id:= 0;
+    exit;
     end;
 
 
-glGenTextures(1, @Surface2Tex^.id);
+glGenTextures(1, @Surface2Atlas^.atlas^.id);
 
-glBindTexture(GL_TEXTURE_2D, Surface2Tex^.id);
+glBindTexture(GL_TEXTURE_2D, Surface2Atlas^.atlas^.id);
 
 if SDL_MustLock(surf) then
     SDLTry(SDL_LockSurface(surf) >= 0, true);
@@ -164,8 +451,8 @@
     tw:= toPowerOf2(Surf^.w);
     th:= toPowerOf2(Surf^.h);
 
-    Surface2Tex^.rx:= Surf^.w / tw;
-    Surface2Tex^.ry:= Surf^.h / th;
+    Surface2Atlas^.atlas^.w:=tw;
+    Surface2Atlas^.atlas^.h:=th;
 
     tmpp:= GetMem(tw * th * surf^.format^.BytesPerPixel);
 
@@ -195,12 +482,12 @@
     end
 else
     begin
-    Surface2Tex^.rx:= 1.0;
-    Surface2Tex^.ry:= 1.0;
+    Surface2Atlas^.atlas^.w:=Surf^.w;
+    Surface2Atlas^.atlas^.h:=Surf^.h;
     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf^.w, surf^.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf^.pixels);
     end;
 
-ResetVertexArrays(Surface2Tex);
+ResetVertexArrays(Surface2Atlas);
 
 if SDL_MustLock(surf) then
     SDL_UnlockSurface(surf);
@@ -212,33 +499,43 @@
 // if nil is passed nothing is done
 procedure FreeTexture(tex: PTexture);
 begin
-if tex <> nil then
+    if tex <> nil then
     begin
-    if tex^.NextTexture <> nil then
-        tex^.NextTexture^.PrevTexture:= tex^.PrevTexture;
-    if tex^.PrevTexture <> nil then
-        tex^.PrevTexture^.NextTexture:= tex^.NextTexture
-    else
-        TextureList:= tex^.NextTexture;
-    glDeleteTextures(1, @tex^.id);
+        FreeTexture(tex^.nextFrame); // free all frames linked to this animation
+
+        if tex^.surface = nil then
+        begin
+            Dispose(tex);
+            exit;
+        end;
+
+        if tex^.shared then
+        begin
+            SDL_FreeSurface(tex^.surface);
+            FreeTexture_(tex); // run atlas side by side for debugging
+            exit;
+        end;
+
+    // Atlas cleanup happens here later on. For now we just free as each sprite has one atlas
+    glDeleteTextures(1, @tex^.atlas^.id);
+    Dispose(tex^.atlas);
+
+    if (tex^.surface <> nil) then
+        SDL_FreeSurface(tex^.surface);
     Dispose(tex);
     end
 end;
 
 procedure initModule;
 begin
-TextureList:= nil;
+assign(logFile, 'out.log');
+rewrite(logFile);
+uAtlas.initModule;
 end;
 
 procedure freeModule;
 begin
-if TextureList <> nil then
-    WriteToConsole('FIXME FIXME FIXME. App shutdown without full cleanup of texture list; read game0.log and please report this problem');
-    while TextureList <> nil do 
-        begin
-        AddFileLog('Texture not freed: width='+inttostr(LongInt(TextureList^.w))+' height='+inttostr(LongInt(TextureList^.h))+' priority='+inttostr(round(TextureList^.priority*1000)));
-        FreeTexture(TextureList);
-        end
+close(logFile);
 end;
 
 end.
--- a/hedgewars/uTypes.pas	Mon Jul 09 23:28:02 2012 -0400
+++ b/hedgewars/uTypes.pas	Tue Jul 10 11:09:38 2012 +0200
@@ -44,7 +44,8 @@
     // Different files are stored in different folders, this enumeration is used to tell which folder to use
     TPathType = (ptNone, ptData, ptGraphics, ptThemes, ptCurrTheme, ptTeams, ptMaps,
             ptMapCurrent, ptDemos, ptSounds, ptGraves, ptFonts, ptForts,
-            ptLocale, ptAmmoMenu, ptHedgehog, ptVoices, ptHats, ptFlags, ptMissionMaps, ptSuddenDeath, ptButtons);
+            ptLocale, ptAmmoMenu, ptHedgehog, ptVoices, ptHats, ptFlags, ptMissionMaps, ptSuddenDeath, ptButtons,
+            ptShaders);
 
     // Available sprites for displaying stuff
     TSprite = (sprWater, sprCloud, sprBomb, sprBigDigit, sprFrame,
@@ -199,14 +200,42 @@
             X, Y: GLint;
             end;
 
+    TMatrix4x4f = array[0..3, 0..3] of GLfloat;
+
+    PAtlas = ^TAtlas;
     PTexture = ^TTexture;
+
+    TAtlas = record
+                id: GLuint;
+                w, h: LongInt;
+                priority: GLfloat;
+            end;
+
+    PVertexRect = ^TVertexRect;
+    TVertexRect = array[0..3] of TVertex2f;
+
+    TCropInformation = record
+        l, r, t, b: Integer; // cropped pixels for each side
+        x, y: Integer;       // pivot (center) of the sprite in the cropped image
+    end;
+    
     TTexture = record
-            id: GLuint;
+            atlas: PAtlas;
             w, h, scale: LongInt;
-            rx, ry: GLfloat;
-            priority: GLfloat;
-            vb, tb: array [0..3] of TVertex2f;
-            PrevTexture, NextTexture: PTexture;
+
+            x, y: LongInt; // Offset in the texture atlas
+            cropInfo: TCropInformation;
+
+            isRotated: boolean; // if true sprite is flipped in the atlas taking w pixels along the y and h pixels along the x axis
+
+            shared: boolean; // true if in an atlas, false if atlas points to a dedicated texture for this sprite
+
+            surface: PSDL_Surface; // retained in memory surface
+
+            // Cached values for texel coordinates and vertex coordinates            
+            vb, tb: TVertexRect;
+
+            nextFrame: PTexture;
             end;
 
     THogEffect = (heInvulnerable, heResurrectable, hePoisoned, heResurrected, heFrozen);
--- a/hedgewars/uVariables.pas	Mon Jul 09 23:28:02 2012 -0400
+++ b/hedgewars/uVariables.pas	Tue Jul 10 11:09:38 2012 +0200
@@ -21,7 +21,7 @@
 unit uVariables;
 interface
 
-uses SDLh, uTypes, uFloat, GLunit, uConsts, Math, uMobile;
+uses SDLh, uTypes, uFloat, GLunit, uConsts, Math, uMobile, uMatrix;
 
 var
 /////// init flags ///////
@@ -225,8 +225,9 @@
         'Graphics/Hats',                 // ptHats
         'Graphics/Flags',                // ptFlags
         'Missions/Maps',                 // ptMissionMaps
-        'Graphics/SuddenDeath',           // ptSuddenDeath
-        'Graphics/Buttons'                // ptButton
+        'Graphics/SuddenDeath',          // ptSuddenDeath
+        'Graphics/Buttons',              // ptButton
+        'Shaders'                        // ptShaders
     );
 
     Fontz: array[THWFont] of THHFont = (
@@ -265,383 +266,384 @@
             Texture: PTexture;
             Surface: PSDL_Surface;
             Width, Height, imageWidth, imageHeight: LongInt;
-            saveSurf: boolean;
+            scale: Integer;
+            saveSurf : boolean;
             priority: GLfloat;
             getDimensions, getImageDimensions: boolean;
             end = (
             (FileName:  'BlueWater'; Path: ptCurrTheme;AltPath: ptGraphics; Texture: nil; Surface: nil;
-            Width:   0; Height:  0; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: true; getImageDimensions: true),// sprWater
+            Width:   0; Height:  0; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: true; getImageDimensions: true),// sprWater
             (FileName:     'Clouds'; Path: ptCurrTheme;AltPath: ptGraphics; Texture: nil; Surface: nil;
-            Width: 256; Height:128; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprCloud
+            Width: 256; Height:128; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprCloud
             (FileName:       'Bomb'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:   8; Height:  8; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprBomb
+            Width:   8; Height:  8; imageWidth: 0; imageHeight: 0; scale: 4; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprBomb
             (FileName:  'BigDigits'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprBigDigit
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprBigDigit
             (FileName:      'Frame'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:   4; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprFrame
+            Width:   4; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprFrame
             (FileName:        'Lag'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  65; Height: 65; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprLag
+            Width:  65; Height: 65; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprLag
             (FileName:      'Arrow'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprCursor
+            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprCursor
             (FileName:'BazookaShell'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprBazookaShell
+            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; scale: 4; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprBazookaShell
             (FileName:    'Targetp'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprTargetP
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprTargetP
             (FileName:        'Bee'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprBee
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprBee
             (FileName: 'SmokeTrace'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprSmokeTrace
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprSmokeTrace
             (FileName:   'RopeHook'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprRopeHook
+            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; scale: 4; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprRopeHook
             (FileName:     'Expl50'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprExplosion50
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprExplosion50
             (FileName:    'MineOff'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:   8; Height:  8; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprMineOff
+            Width:   8; Height:  8; imageWidth: 0; imageHeight: 0; scale: 4; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprMineOff
             (FileName:     'MineOn'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:   8; Height:  8; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprMineOn
+            Width:   8; Height:  8; imageWidth: 0; imageHeight: 0; scale: 4; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprMineOn
             (FileName:     'MineDead'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:   8; Height:  8; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprMineDead
+            Width:   8; Height:  8; imageWidth: 0; imageHeight: 0; scale: 4; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprMineDead
             (FileName:       'Case'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  48; Height: 48; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprCase
+            Width:  48; Height: 48; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprCase
             (FileName:   'FirstAid'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  48; Height: 48; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprFAid
+            Width:  48; Height: 48; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprFAid
             (FileName:   'dynamite'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprDynamite
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprDynamite
             (FileName:      'Power'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprPower
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprPower
             (FileName:     'ClBomb'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprClusterBomb
+            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprClusterBomb
             (FileName: 'ClParticle'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprClusterParticle
+            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprClusterParticle
             (FileName:      'Flame'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprFlame
+            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprFlame
             (FileName:   'horizont'; Path: ptCurrTheme;AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:   0; Height:  0; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: true; getImageDimensions: true),// sprHorizont
+            Width:   0; Height:  0; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: true; getImageDimensions: true),// sprHorizont
             (FileName:  'horizontL'; Path: ptCurrTheme;AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:   0; Height:  0; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: true; getImageDimensions: true),// sprHorizont
+            Width:   0; Height:  0; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: true; getImageDimensions: true),// sprHorizont
             (FileName:  'horizontR'; Path: ptCurrTheme;AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:   0; Height:  0; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: true; getImageDimensions: true),// sprHorizont
+            Width:   0; Height:  0; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: true; getImageDimensions: true),// sprHorizont
             (FileName:        'Sky'; Path: ptCurrTheme;AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:   0; Height:  0; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: true; getImageDimensions: true),// sprSky
+            Width:   0; Height:  0; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: true; getImageDimensions: true),// sprSky
             (FileName:       'SkyL'; Path: ptCurrTheme;AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:   0; Height:  0; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: true; getImageDimensions: true),// sprSky
+            Width:   0; Height:  0; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: true; getImageDimensions: true),// sprSky
             (FileName:       'SkyR'; Path: ptCurrTheme;AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:   0; Height:  0; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: true; getImageDimensions: true),// sprSky
+            Width:   0; Height:  0; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: true; getImageDimensions: true),// sprSky
             (FileName:   'Slot'; Path: ptAmmoMenu; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: true; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprAMSlot
+            Width: 32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: true; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprAMSlot
             (FileName:      'Ammos'; Path: ptAmmoMenu; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: true; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprAMAmmos
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: true; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprAMAmmos
             (FileName:   'Ammos_bw'; Path: ptAmmoMenu; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: true; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprAMAmmosBW
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: true; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprAMAmmosBW
             (FileName:   'SlotKeys'; Path: ptAmmoMenu; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprAMSlotKeys
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprAMSlotKeys
             (FileName:  'Corners'; Path: ptAmmoMenu; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  2; Height: 2; imageWidth: 0; imageHeight: 0; saveSurf: true; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprAMCorners
+            Width:  2; Height: 2; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: true; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprAMCorners
             (FileName:     'Finger'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 48; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprFinger
+            Width:  32; Height: 48; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprFinger
             (FileName:    'AirBomb'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprAirBomb
+            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; scale: 4; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprAirBomb
             (FileName:   'Airplane'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 256; Height: 128; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprAirplane
+            Width: 256; Height: 128; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprAirplane
             (FileName: 'amAirplane'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprAmAirplane
+            Width:  64; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprAmAirplane
             (FileName:   'amGirder'; Path: ptCurrTheme; AltPath: ptGraphics; Texture: nil; Surface: nil;
-            Width: 160; Height:160; imageWidth: 0; imageHeight: 0; saveSurf:  true; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprAmGirder
+            Width: 160; Height:160; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf:  true; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprAmGirder
             (FileName:     'hhMask'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf:  true; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHHTelepMask
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf:  true; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHHTelepMask
             (FileName:     'Switch'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprSwitch
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprSwitch
             (FileName:  'Parachute'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  48; Height: 48; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprParachute
+            Width:  48; Height: 48; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprParachute
             (FileName:     'Target'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprTarget
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprTarget
             (FileName:   'RopeNode'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:   6; Height:  6; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprRopeNode
+            Width:   6; Height:  6; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprRopeNode
             (FileName:   'thinking'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprQuestion
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprQuestion
             (FileName:   'PowerBar'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 256; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprPowerBar
+            Width: 256; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprPowerBar
             (FileName:    'WindBar'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 151; Height: 17; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprWindBar
+            Width: 151; Height: 17; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprWindBar
             (FileName:      'WindL'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  80; Height: 13; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprWindL
+            Width:  80; Height: 13; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprWindL
             (FileName:      'WindR'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  80; Height: 13; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprWindR
+            Width:  80; Height: 13; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprWindR
 {$IFDEF USE_TOUCH_INTERFACE}
             (FileName: 'firebutton'; Path: ptButtons; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 128; Height: 128; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true), // sprFireButton
+            Width: 128; Height: 128; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true), // sprFireButton
             (FileName: 'arrowUp'; Path: ptButtons; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 100; Height: 100; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true), // sprArrowUp
+            Width: 100; Height: 100; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true), // sprArrowUp
             (FileName: 'arrowDown'; Path: ptButtons; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 100; Height: 100; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true), // sprArrowDown
+            Width: 100; Height: 100; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true), // sprArrowDown
             (FileName: 'arrowLeft'; Path: ptButtons; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 100; Height: 100; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true), // sprArrowLeft
+            Width: 100; Height: 100; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true), // sprArrowLeft
             (FileName: 'arrowRight'; Path: ptButtons; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 100; Height: 100; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true), // sprArrowRight
+            Width: 100; Height: 100; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true), // sprArrowRight
             (FileName: 'forwardjump'; Path: ptButtons; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 128; Height: 128; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true), // sprAMWidget
+            Width: 128; Height: 128; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true), // sprAMWidget
             (FileName: 'backjump'; Path: ptButtons; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 128; Height: 128; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true), // sprJumpWidget
+            Width: 128; Height: 128; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true), // sprJumpWidget
             (FileName: 'pause'; Path: ptButtons; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 120; Height: 100; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true), // sprPauseButton
+            Width: 120; Height: 100; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true), // sprPauseButton
             (FileName: 'pause'; Path: ptButtons; AltPath: ptNone; Texture: nil; Surface: nil;//TODO correct image
-            Width: 120; Height: 128; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true), // sprTimerButton
+            Width: 120; Height: 128; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true), // sprTimerButton
             (FileName: 'forwardjump'; Path: ptButtons; AltPath: ptNone; Texture: nil; Surface: nil;//TODO correct image
-            Width: 120; Height: 128; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true), // sprTargetButton
+            Width: 120; Height: 128; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true), // sprTargetButton
 {$ENDIF}
             (FileName:      'Flake'; Path:ptCurrTheme; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprFlake
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprFlake
             (FileName:     'amRope'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandRope
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandRope
             (FileName:  'amBazooka'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandBazooka
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandBazooka
             (FileName:  'amShotgun'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandShotgun
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandShotgun
             (FileName:   'amDEagle'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandDEagle
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandDEagle
             (FileName:'amAirAttack'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandAirAttack
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandAirAttack
             (FileName: 'amBaseball'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandBaseball
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandBaseball
             (FileName:     'Hammer'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprPHammer
+            Width:  32; Height: 64; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprPHammer
             (FileName: 'amBTorch_i'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandBlowTorch
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandBlowTorch
             (FileName: 'amBTorch_w'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprBlowTorch
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprBlowTorch
             (FileName:   'Teleport'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprTeleport
+            Width:  64; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprTeleport
             (FileName:    'HHDeath'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprHHDeath
+            Width:  32; Height: 64; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprHHDeath
             (FileName:'amShotgun_w'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprShotgun
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprShotgun
             (FileName: 'amDEagle_w'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprDEagle
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprDEagle
             (FileName:       'Idle'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprHHIdle
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprHHIdle
             (FileName:     'Mortar'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprMortar
+            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; scale: 4; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprMortar
             (FileName:  'TurnsLeft'; Path: ptAmmoMenu; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; saveSurf: true; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprTurnsLeft
+            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: true; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprTurnsLeft
             (FileName: 'amKamikaze'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 128; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprKamikaze
+            Width: 128; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprKamikaze
             (FileName:     'amWhip'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 128; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprWhip
+            Width: 128; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprWhip
             (FileName:     'Kowtow'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprKowtow
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprKowtow
             (FileName:        'Sad'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprSad
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprSad
             (FileName:       'Wave'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprWave
+            Width:  64; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprWave
             (FileName:     'Hurrah'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprHurrah
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprHurrah
             (FileName:'ILoveLemonade';Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 128; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprLemonade
+            Width: 128; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprLemonade
             (FileName:      'Shrug'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 32;  Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprShrug
+            Width: 32;  Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprShrug
             (FileName:     'Juggle'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 32;  Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprJuggle
+            Width: 32;  Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprJuggle
             (FileName:   'ExplPart'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprExplPart
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprExplPart
             (FileName:  'ExplPart2'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprExplPart2
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprExplPart2
             (FileName:  'Cake_walk'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprCakeWalk
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprCakeWalk
             (FileName:  'Cake_down'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprCakeDown
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprCakeDown
             (FileName: 'Watermelon'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprWatermelon
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprWatermelon
             (FileName:  'EvilTrace'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprEvilTrace
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprEvilTrace
             (FileName:'HellishBomb'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHellishBomb
+            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHellishBomb
             (FileName:  'Seduction'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprSeduction
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprSeduction
             (FileName:    'HHDress'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprDress
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprDress
             (FileName:   'Censored'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 16; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprCensored
+            Width:  64; Height: 16; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprCensored
             (FileName:      'Drill'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprDrill
+            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; scale: 4; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprDrill
             (FileName:    'amDrill'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandDrill
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandDrill
             (FileName:  'amBallgun'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandBallgun
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandBallgun
             (FileName:      'Balls'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 20; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprBalls
+            Width:  32; Height: 20; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprBalls
             (FileName:    'RCPlane'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprPlane
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprPlane
             (FileName:  'amRCPlane'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandPlane
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandPlane
             (FileName:    'Utility'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  48; Height: 48; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprUtility
+            Width:  48; Height: 48; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprUtility
             (FileName:'Invulnerable';Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  48; Height: 48; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprInvulnerable
+            Width:  48; Height: 48; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprInvulnerable
             (FileName:   'Vampiric'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  48; Height: 48; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprVampiric
+            Width:  48; Height: 48; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprVampiric
             (FileName:   'amGirder'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 512; Height:512; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprGirder
+            Width: 512; Height:512; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprGirder
             (FileName:'SpeechCorner';Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  12; Height: 9; imageWidth: 0; imageHeight: 0; saveSurf:  true; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprSpeechCorner
+            Width:  12; Height: 9; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf:  true; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprSpeechCorner
             (FileName: 'SpeechEdge'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  25; Height: 9; imageWidth: 0; imageHeight: 0; saveSurf:  true; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprSpeechEdge
+            Width:  25; Height: 9; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf:  true; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprSpeechEdge
             (FileName: 'SpeechTail'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  25; Height: 26; imageWidth: 0; imageHeight: 0; saveSurf: true; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprSpeechTail
+            Width:  25; Height: 26; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: true; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprSpeechTail
             (FileName:'ThoughtCorner';Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  49; Height: 37; imageWidth: 0; imageHeight: 0; saveSurf: true; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprThoughtCorner
+            Width:  49; Height: 37; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: true; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprThoughtCorner
             (FileName:'ThoughtEdge'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  23; Height: 16; imageWidth: 0; imageHeight: 0; saveSurf: true; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprThoughtEdge
+            Width:  23; Height: 16; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: true; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprThoughtEdge
             (FileName:'ThoughtTail'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  45; Height: 65; imageWidth: 0; imageHeight: 0; saveSurf: true; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprThoughtTail
+            Width:  45; Height: 65; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: true; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprThoughtTail
             (FileName:'ShoutCorner'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  34; Height: 23; imageWidth: 0; imageHeight: 0; saveSurf: true; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprShoutCorner
+            Width:  34; Height: 23; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: true; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprShoutCorner
             (FileName:  'ShoutEdge'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  30; Height: 20; imageWidth: 0; imageHeight: 0; saveSurf: true; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprShoutEdge
+            Width:  30; Height: 20; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: true; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprShoutEdge
             (FileName:  'ShoutTail'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  30; Height: 37; imageWidth: 0; imageHeight: 0; saveSurf: true; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprShoutTail
+            Width:  30; Height: 37; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: true; priority: tpLowest; getDimensions: false; getImageDimensions: true),// sprShoutTail
             (FileName:'amSniperRifle';Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 128; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprSniperRifle
+            Width: 128; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprSniperRifle
             (FileName:    'Bubbles'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprBubbles
+            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprBubbles
             (FileName:  'amJetpack'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprJetpack
+            Width: 64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprJetpack
             (FileName:  'Health'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 16; Height: 16; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprHealth
+            Width: 16; Height: 16; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprHealth
             (FileName:  'amMolotov'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),//sprHandMolotov
+            Width: 32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),//sprHandMolotov
             (FileName:  'Molotov'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprMolotov
+            Width: 32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprMolotov
             (FileName: 'Smoke'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  22; Height: 22; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprSmoke
+            Width:  22; Height: 22; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprSmoke
             (FileName: 'SmokeWhite'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  22; Height: 22; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprSmokeWhite
+            Width:  22; Height: 22; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprSmokeWhite
             (FileName: 'Shells'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  8; Height: 8; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpLow; getDimensions: false; getImageDimensions: true),// sprShell
+            Width:  8; Height: 8; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpLow; getDimensions: false; getImageDimensions: true),// sprShell
             (FileName: 'Dust'; Path: ptCurrTheme; AltPath: ptGraphics; Texture: nil; Surface: nil;
-            Width:  22; Height: 22; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprDust
+            Width:  22; Height: 22; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprDust
             (FileName: 'SnowDust'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  22; Height: 22; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprSnowDust
+            Width:  22; Height: 22; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprSnowDust
             (FileName: 'Explosives'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  48; Height: 48; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprExplosives
+            Width:  48; Height: 48; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprExplosives
             (FileName: 'ExplosivesRoll'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  48; Height: 48; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprExplosivesRoll
+            Width:  48; Height: 48; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprExplosivesRoll
             (FileName: 'amTeleport'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprAmTeleport
+            Width:  64; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprAmTeleport
             (FileName: 'Splash'; Path: ptCurrTheme; AltPath: ptGraphics; Texture: nil; Surface: nil;
-            Width:  80; Height: 50; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprSplash
+            Width:  80; Height: 50; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprSplash
             (FileName: 'Droplet'; Path: ptCurrTheme; AltPath: ptGraphics; Texture: nil; Surface: nil;
-            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprDroplet
+            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprDroplet
             (FileName: 'Birdy'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  75; Height: 75; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprBirdy
+            Width:  75; Height: 75; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprBirdy
             (FileName:  'amCake'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandCake
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandCake
             (FileName:  'amConstruction'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandConstruction
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandConstruction
             (FileName:  'amGrenade'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandGrenade
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandGrenade
             (FileName:  'amMelon'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandMelon
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandMelon
             (FileName:  'amMortar'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandMortar
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandMortar
             (FileName:  'amSkip'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandSkip
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandSkip
             (FileName:  'amCluster'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandCluster
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandCluster
             (FileName:  'amDynamite'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandDynamite
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandDynamite
             (FileName:  'amHellish'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandHellish
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandHellish
             (FileName:  'amMine'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandMine
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandMine
             (FileName:  'amSeduction'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandSeduction
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandSeduction
             (FileName:  'amVamp'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  128; Height: 128; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandVamp
+            Width:  128; Height: 128; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandVamp
             (FileName:  'BigExplosion'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  385; Height: 385; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprBigExplosion
+            Width:  385; Height: 385; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprBigExplosion
             (FileName:  'SmokeRing'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  200; Height: 200; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprSmokeRing
+            Width:  200; Height: 200; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprSmokeRing
             (FileName:  'BeeTrace'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprBeeTrace
+            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprBeeTrace
             (FileName:  'Egg'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprEgg
+            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprEgg
             (FileName:  'TargetBee'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprTargetBee
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprTargetBee
             (FileName:  'amBee'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  128; Height: 128; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandBee
+            Width:  128; Height: 128; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandBee
             (FileName:  'Feather'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  15; Height: 25; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprFeather
+            Width:  15; Height: 25; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprFeather
             (FileName:  'Piano'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  128; Height: 128; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprPiano
+            Width:  128; Height: 128; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprPiano
             (FileName:  'amSineGun'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  128; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandSineGun
+            Width:  128; Height: 64; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandSineGun
             (FileName:  'amPortalGun'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 128; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprPortalGun
+            Width: 128; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprPortalGun
             (FileName:  'Portal'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprPortal
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprPortal
             (FileName:  'cheese'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprCheese
+            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprCheese
             (FileName:  'amCheese'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandCheese
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandCheese
             (FileName:  'amFlamethrower'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  128; Height: 128; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandFlamethrower
+            Width:  128; Height: 128; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandFlamethrower
             (FileName:  'Chunk'; Path: ptCurrTheme; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprChunk
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprChunk
             (FileName:  'Note'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprNote
+            Width:  32; Height: 32; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprNote
             (FileName:   'SMineOff'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:   8; Height:  8; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprSMineOff
+            Width:   8; Height:  8; imageWidth: 0; imageHeight: 0; scale: 4; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprSMineOff
             (FileName:    'SMineOn'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:   8; Height:  8; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprSMineOn
+            Width:   8; Height:  8; imageWidth: 0; imageHeight: 0; scale: 4; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprSMineOn
             (FileName:   'amSMine'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandSMine
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandSMine
             (FileName:  'amHammer'; Path: ptHedgehog; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 128; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true), // sprHammer
+            Width: 128; Height: 64; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true), // sprHammer
             (FileName: 'amResurrector'; Path: ptHedgehog; AltPath: ptNone;
                 Texture: nil; Surface: nil; Width: 32; Height: 32;
-                imageWidth: 0; imageHeight: 0; saveSurf: false; priority:
+                imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority:
                 tpMedium; getDimensions: false; getImageDimensions: true),
             //sprHandResurrector
             (FileName: 'Cross'; Path: ptGraphics; AltPath: ptNone;
                 Texture: nil; Surface: nil; Width: 108; Height: 138;
-                imageWidth: 0; imageHeight: 0; saveSurf: false; priority:
+                imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority:
                 tpMedium; getDimensions: false; getImageDimensions: true),
             //sprCross
             (FileName:  'AirDrill'; Path: ptGraphics; AltPath: ptNone;
                 Texture: nil; Surface: nil; Width:  16; Height: 16;
-                imageWidth: 0; imageHeight: 0; saveSurf: false; priority:
+                imageWidth: 0; imageHeight: 0; scale: 4; saveSurf: false; priority:
                 tpMedium; getDimensions: false; getImageDimensions: true),
             // sprAirDrill
             (FileName:  'NapalmBomb'; Path: ptGraphics; AltPath: ptNone;
                 Texture: nil; Surface: nil; Width:  16; Height: 16;
-                imageWidth: 0; imageHeight: 0; saveSurf: false; priority:
+                imageWidth: 0; imageHeight: 0; scale: 4; saveSurf: false; priority:
                 tpMedium; getDimensions: false; getImageDimensions: true),
             // sprNapalmBomb
             (FileName:  'BulletHit'; Path: ptGraphics; AltPath: ptNone;
                 Texture: nil; Surface: nil; Width:  32; Height: 32;
-                imageWidth: 0; imageHeight: 0; saveSurf: false; priority:
+                imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority:
                 tpMedium; getDimensions: false; getImageDimensions: true),
             // sprNapalmBomb
             (FileName:  'Snowball'; Path: ptCurrTheme; AltPath: ptGraphics; Texture: nil; Surface: nil;
-            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprSnowball
+            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprSnowball
             (FileName:  'amSnowball'; Path: ptCurrTheme; AltPath: ptHedgehog; Texture: nil; Surface: nil;
-            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandSnowball
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprHandSnowball
             (FileName:  'Snow'; Path: ptCurrTheme; AltPath: ptGraphics; Texture: nil; Surface: nil;
-            Width:  4; Height: 4; imageWidth: 0; imageHeight: 0; saveSurf: true; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprSnow
+            Width:  4; Height: 4; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: true; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprSnow
             (FileName:    'SDFlake'; Path: ptCurrTheme; AltPath: ptSuddenDeath; Texture: nil; Surface: nil;
-            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprSDFlake
+            Width:  64; Height: 64; imageWidth: 0; imageHeight: 0; scale: 2; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprSDFlake
             (FileName:    'SDWater'; Path: ptCurrTheme; AltPath: ptSuddenDeath; Texture: nil; Surface: nil;
-            Width:   0; Height:  0; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: true; getImageDimensions: true),// sprSDWater
+            Width:   0; Height:  0; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: true; getImageDimensions: true),// sprSDWater
             (FileName:   'SDClouds'; Path: ptCurrTheme; AltPath: ptSuddenDeath; Texture: nil; Surface: nil;
-            Width: 256; Height:128; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprSDCloud
+            Width: 256; Height:128; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHigh; getDimensions: false; getImageDimensions: true),// sprSDCloud
             (FileName:   'SDSplash'; Path: ptCurrTheme; AltPath: ptSuddenDeath; Texture: nil; Surface: nil;
-            Width:  80; Height: 50; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprSDSplash
+            Width:  80; Height: 50; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpMedium; getDimensions: false; getImageDimensions: true),// sprSDSplash
             (FileName:  'SDDroplet'; Path: ptCurrTheme; AltPath: ptSuddenDeath; Texture: nil; Surface: nil;
-            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprSDDroplet
+            Width:  16; Height: 16; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprSDDroplet
             (FileName:  'TARDIS'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width:  48; Height: 79; imageWidth: 0; imageHeight: 0; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprTardis
+            Width:  48; Height: 79; imageWidth: 0; imageHeight: 0; scale: 1; saveSurf: false; priority: tpHighest; getDimensions: false; getImageDimensions: true),// sprTardis
             (FileName:  'slider'; Path: ptGraphics; AltPath: ptNone; Texture: nil; Surface: nil;
-            Width: 3; Height: 17; imageWidth: 3; imageHeight: 17; saveSurf: false; priority: tpLow; getDimensions: false; getImageDimensions: false) // sprSlider
+            Width: 3; Height: 17; imageWidth: 3; imageHeight: 17; scale: 1; saveSurf: false; priority: tpLow; getDimensions: false; getImageDimensions: false) // sprSlider
             );
 
 const
@@ -2427,6 +2429,7 @@
     SyncTexture,
     ConfirmTexture: PTexture;
     cScaleFactor: GLfloat;
+    cStereoDepth: GLfloat;
     SupportNPOTT: Boolean;
     Step: LongInt;
     squaresize : LongInt;
@@ -2465,6 +2468,9 @@
 
     lastTurnChecksum : Longword;
 
+    mModelview: TMatrix4x4f;
+    mProjection: TMatrix4x4f;
+
 var trammo:  array[TAmmoStrId] of ansistring;   // name of the weapon
     trammoc: array[TAmmoStrId] of ansistring;   // caption of the weapon
     trammod: array[TAmmoStrId] of ansistring;   // description of the weapon
@@ -2613,6 +2619,9 @@
     SDWaterOpacity:= $80;
 
     LuaGoals:= '';
+
+    MatrixLoadIdentity(mModelview);
+    MatrixLoadIdentity(mProjection);
 end;
 
 procedure freeModule;
--- a/hedgewars/uWorld.pas	Mon Jul 09 23:28:02 2012 -0400
+++ b/hedgewars/uWorld.pas	Tue Jul 10 11:09:38 2012 +0200
@@ -60,7 +60,8 @@
     uCaptions,
     uCursor,
     uCommands,
-    uMobile
+    uMobile,
+    uAtlas
     ;
 
 var cWaveWidth, cWaveHeight: LongInt;
@@ -77,7 +78,6 @@
     amSel: TAmmoType = amNothing;
     missionTex: PTexture;
     missionTimer: LongInt;
-    stereoDepth: GLfloat;
     isFirstFrame: boolean;
     AMAnimType: LongInt;
 
@@ -495,8 +495,7 @@
 DrawLine2Surf(amSurface, AMRect.w+BORDERSIZE+i, BORDERSIZE, AMRect.w + BORDERSIZE+i, AMRect.h + BORDERSIZE, 160,160,160);//right
 end;
 
-GetAmmoMenuTexture:= Surface2Tex(amSurface, false);
-if amSurface <> nil then SDL_FreeSurface(amSurface);
+GetAmmoMenuTexture:= Surface2Atlas(amSurface, false);
 end;
 
 procedure ShowAmmoMenu;
@@ -795,29 +794,30 @@
         VertexBuffer[3].X:= -lw;
         VertexBuffer[3].Y:= lh;
 
-        glDisableClientState(GL_TEXTURE_COORD_ARRAY);
-        glEnableClientState(GL_COLOR_ARRAY);
+        BeginWater;        
         if SuddenDeathDmg then
-            glColorPointer(4, GL_UNSIGNED_BYTE, 0, @SDWaterColorArray[0])
+            SetColorPointer(@SDWaterColorArray[0])
         else
-            glColorPointer(4, GL_UNSIGNED_BYTE, 0, @WaterColorArray[0]);
+            SetColorPointer(@WaterColorArray[0]);
 
-        glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]);
+        SetVertexPointer(@VertexBuffer[0]);
 
         glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer));
 
-        glDisableClientState(GL_COLOR_ARRAY);
-        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+        EndWater;
+        {$IFNDEF GL2}
         glColor4ub($FF, $FF, $FF, $FF); // must not be Tint() as color array seems to stay active and color reset is required
+        {$ENDIF}
         glEnable(GL_TEXTURE_2D);
     end;
 end;
 
 procedure DrawWaves(Dir, dX, dY: LongInt; tnt: Byte);
 var VertexBuffer, TextureBuffer: array [0..3] of TVertex2f;
-    lw, waves, shift: GLfloat;
+    lw, waves: GLfloat;
     sprite: TSprite;
-begin
+    r: TSDL_Rect;
+begin{
 if SuddenDeathDmg then
     sprite:= sprSDWater
 else
@@ -826,7 +826,6 @@
 cWaveWidth:= SpritesData[sprite].Width;
 
 lw:= cScreenWidth / cScaleFactor;
-waves:= lw * 2 / cWaveWidth;
 
 if SuddenDeathDmg then
     Tint(LongInt(tnt) * SDWaterColorArray[2].r div 255 + 255 - tnt,
@@ -841,7 +840,7 @@
          255
     );
 
-glBindTexture(GL_TEXTURE_2D, SpritesData[sprite].Texture^.id);
+glBindTexture(GL_TEXTURE_2D, SpritesData[sprite].Texture^.atlas^.id);
 
 VertexBuffer[0].X:= -lw;
 VertexBuffer[0].Y:= cWaterLine + WorldDy + dY;
@@ -852,19 +851,16 @@
 VertexBuffer[3].X:= -lw;
 VertexBuffer[3].Y:= VertexBuffer[2].Y;
 
-shift:= - lw / cWaveWidth;
-TextureBuffer[0].X:= shift + (( - WorldDx + LongInt(RealTicks shr 6) * Dir + dX) mod cWaveWidth) / (cWaveWidth - 1);
-TextureBuffer[0].Y:= 0;
-TextureBuffer[1].X:= TextureBuffer[0].X + waves;
-TextureBuffer[1].Y:= TextureBuffer[0].Y;
-TextureBuffer[2].X:= TextureBuffer[1].X;
-TextureBuffer[2].Y:= SpritesData[sprite].Texture^.ry;
-TextureBuffer[3].X:= TextureBuffer[0].X;
-TextureBuffer[3].Y:= TextureBuffer[2].Y;
+// this uses texture repeat mode, when using an atlas rect we need to split to several quads here!
+r.x := -Trunc(lw) + (( - WorldDx + LongInt(RealTicks shr 6) * Dir + dX) mod cWaveWidth);
+r.y:= 0;
+r.w:= Trunc(lw + lw);
+r.h:= SpritesData[sprite].Texture^.h;
+ComputeTexcoords(SpritesData[sprite].Texture, @r, @TextureBuffer);
 
 
-glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]);
-glTexCoordPointer(2, GL_FLOAT, 0, @TextureBuffer[0]);
+SetVertexPointer(@VertexBuffer[0]);
+SetTexCoordPointer(@TextureBuffer[0]);
 glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer));
 
 Tint($FF, $FF, $FF, $FF);
@@ -873,7 +869,7 @@
     DrawSprite(sprWater,
         i * cWaveWidth + ((WorldDx + (RealTicks shr 6) * Dir + dX) mod cWaveWidth) - (cScreenWidth div 2),
         cWaterLine + WorldDy + dY,
-        0)}
+        0)}}
 end;
 
 procedure DrawRepeated(spr, sprL, sprR: TSprite; Shift, OffsetY: LongInt);
@@ -1069,41 +1065,26 @@
         else
             glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE);
         DrawWorldStereo(Lag, rmRightEye);
-        end
+        end;
+{$ELSE}
+    ;
 {$ENDIF}
+    DebugAtlas;
 end;
 
 procedure ChangeDepth(rm: TRenderMode; d: GLfloat);
 begin
-{$IFDEF S3D_DISABLED}
-    rm:= rm; d:= d; // avoid hint
-    exit;
-{$ELSE}
     d:= d / 5;
-    if rm = rmDefault then
-        exit
-    else if rm = rmLeftEye then
+    if rm = rmLeftEye then
         d:= -d;
-    stereoDepth:= stereoDepth + d;
-    glMatrixMode(GL_PROJECTION);
-    glTranslatef(d, 0, 0);
-    glMatrixMode(GL_MODELVIEW);
-{$ENDIF}
+    cStereoDepth:= cStereoDepth + d;
+    UpdateProjection;
 end;
  
 procedure ResetDepth(rm: TRenderMode);
 begin
-{$IFDEF S3D_DISABLED}
-    rm:= rm; // avoid hint
-    exit;
-{$ELSE}
-    if rm = rmDefault then
-        exit;
-    glMatrixMode(GL_PROJECTION);
-    glTranslatef(-stereoDepth, 0, 0);
-    glMatrixMode(GL_MODELVIEW);
-    stereoDepth:= 0;
-{$ENDIF}
+    cStereoDepth:= 0;
+    UpdateProjection;
 end;
  
 procedure DrawWorldStereo(Lag: LongInt; RM: TRenderMode);
@@ -1496,8 +1477,7 @@
         tmpSurface:= TTF_RenderUTF8_Blended(Fontz[fnt16].Handle, Str2PChar(s), cWhiteColorChannels);
         tmpSurface:= doSurfaceConversion(tmpSurface);
         FreeTexture(timeTexture);
-        timeTexture:= Surface2Tex(tmpSurface, false);
-        SDL_FreeSurface(tmpSurface)
+        timeTexture:= Surface2Atlas(tmpSurface, false);
         end;
 
     if timeTexture <> nil then
@@ -1514,8 +1494,7 @@
             tmpSurface:= TTF_RenderUTF8_Blended(Fontz[fnt16].Handle, Str2PChar(s), cWhiteColorChannels);
             tmpSurface:= doSurfaceConversion(tmpSurface);
             FreeTexture(fpsTexture);
-            fpsTexture:= Surface2Tex(tmpSurface, false);
-            SDL_FreeSurface(tmpSurface)
+            fpsTexture:= Surface2Atlas(tmpSurface, false);
             end;
         if fpsTexture <> nil then
             DrawTexture((cScreenWidth shr 1) - 60 - offsetY, offsetX, fpsTexture);
@@ -1569,7 +1548,7 @@
 
         glDisable(GL_TEXTURE_2D);
 
-        glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]);
+        SetVertexPointer(@VertexBuffer[0]);
         glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer));
 
         glEnable(GL_TEXTURE_2D);
@@ -1838,14 +1817,13 @@
     missionTimer:= 0;
     missionTex:= nil;
     cOffsetY:= 0;
-    stereoDepth:= 0;
+    cStereoDepth:= 0;
     AMState:= AMHidden;
     isFirstFrame:= true;
 end;
 
 procedure freeModule;
 begin
-    stereoDepth:= stereoDepth; // avoid hint
     FreeTexture(fpsTexture);
     fpsTexture:= nil;
     FreeTexture(timeTexture);
--- a/share/hedgewars/Data/CMakeLists.txt	Mon Jul 09 23:28:02 2012 -0400
+++ b/share/hedgewars/Data/CMakeLists.txt	Tue Jul 10 11:09:38 2012 +0200
@@ -1,3 +1,3 @@
-foreach(dir "Fonts" "Forts" "Graphics" "Locale" "Maps" "Music" "Sounds" "Themes" "Missions" "Names" "misc" "Scripts")
+foreach(dir "Fonts" "Forts" "Graphics" "Locale" "Maps" "Music" "Sounds" "Themes" "Missions" "Names" "misc" "Scripts" "Shaders")
   add_subdirectory(${dir})
 endforeach(dir)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/hedgewars/Data/Shaders/CMakeLists.txt	Tue Jul 10 11:09:38 2012 +0200
@@ -0,0 +1,5 @@
+file(GLOB BaseShaders *.vs *.fs)
+
+install(FILES
+	${BaseShaders}
+	DESTINATION ${SHAREPATH}Data/Shaders)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/hedgewars/Data/Shaders/default.fs	Tue Jul 10 11:09:38 2012 +0200
@@ -0,0 +1,13 @@
+#version 130
+
+uniform sampler2D tex0;
+uniform vec4 tint;
+
+in vec2 tex;
+
+out vec4 color;
+
+void main()
+{
+    color = texture(tex0, tex) * tint;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/hedgewars/Data/Shaders/default.vs	Tue Jul 10 11:09:38 2012 +0200
@@ -0,0 +1,16 @@
+#version 130
+
+in vec2 vertex;
+in vec2 texcoord;
+in vec4 colors;
+
+out vec2 tex;
+
+uniform mat4 mvp;
+
+void main()
+{
+    vec4 p = mvp * vec4(vertex, 0.0f, 1.0f);
+    gl_Position = p;
+    tex = texcoord;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/hedgewars/Data/Shaders/water.fs	Tue Jul 10 11:09:38 2012 +0200
@@ -0,0 +1,9 @@
+#version 130
+
+in vec4 vcolor;
+out vec4 color;
+
+void main()
+{
+    color = vcolor;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/share/hedgewars/Data/Shaders/water.vs	Tue Jul 10 11:09:38 2012 +0200
@@ -0,0 +1,14 @@
+#version 130
+
+in vec2 vertex;
+in vec4 color;
+out vec4 vcolor;
+
+uniform mat4 mvp;
+
+void main()
+{
+    vec4 p = mvp * vec4(vertex, 0.0f, 1.0f);
+    gl_Position = p;
+    vcolor = color;
+}