Merge
authorWolfgang Steffens <WolfgangSteff@gmail.com>
Mon, 25 Jun 2012 10:44:27 +0200
changeset 7288 5d0704f23a2a
parent 7188 580cd247511e (diff)
parent 7285 a9016b157535 (current diff)
child 7291 ad4b6c2b09e8
Merge
hedgewars/options.inc
hedgewars/uGearsRender.pas
hedgewars/uTypes.pas
hedgewars/uVariables.pas
hedgewars/uWorld.pas
share/hedgewars/Data/Scripts/plist/Balanced Random Weapon.plist
share/hedgewars/Data/Scripts/plist/Capture the Flag.plist
share/hedgewars/Data/Scripts/plist/Highlander.plist
share/hedgewars/Data/Scripts/plist/No Jumping.plist
share/hedgewars/Data/Scripts/plist/Normal.plist
share/hedgewars/Data/Scripts/plist/Racer.plist
share/hedgewars/Data/Scripts/plist/Random Weapon.plist
share/hedgewars/Data/Scripts/plist/Space Invasion.plist
share/hedgewars/Data/Scripts/plist/The Specialists.plist
share/hedgewars/Data/Scripts/plist/Tumbler.plist
--- a/hedgewars/options.inc	Mon Jun 25 10:16:19 2012 +0400
+++ b/hedgewars/options.inc	Mon Jun 25 10:44:27 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}
--- a/hedgewars/uChat.pas	Mon Jun 25 10:16:19 2012 +0400
+++ b/hedgewars/uChat.pas	Mon Jun 25 10:44:27 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;
 
--- a/hedgewars/uGearsRender.pas	Mon Jun 25 10:16:19 2012 +0400
+++ b/hedgewars/uGearsRender.pas	Mon Jun 25 10:44:27 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
--- a/hedgewars/uLandTexture.pas	Mon Jun 25 10:16:19 2012 +0400
+++ b/hedgewars/uLandTexture.pas	Mon Jun 25 10:44:27 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	Mon Jun 25 10:44:27 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 Jun 25 10:16:19 2012 +0400
+++ b/hedgewars/uRender.pas	Mon Jun 25 10:44:27 2012 +0200
@@ -22,7 +22,7 @@
 
 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);
@@ -48,14 +48,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
@@ -71,8 +135,7 @@
 
 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;
+    VertexBuffer, TextureBuffer: TVertexRect;
 begin
 if (SourceTexture^.h = 0) or (SourceTexture^.w = 0) then
     exit;
@@ -88,12 +151,9 @@
 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;
@@ -104,17 +164,8 @@
 VertexBuffer[3].X:= X;
 VertexBuffer[3].Y:= rr.h + Y;
 
-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]);
+SetVertexPointer(@VertexBuffer[0]);
+SetTexCoordPointer(@TextureBuffer[0]);
 glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer));
 end;
 
@@ -125,18 +176,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,8 +195,8 @@
 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;
 begin
 // do not draw anything outside the visible screen space (first check fixes some sprite drawing, e.g. hedgehogs)
@@ -155,14 +205,13 @@
 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);
@@ -172,12 +221,13 @@
 nx:= round(Texture^.w / w); // number of horizontal frames
 ny:= round(Texture^.h / h); // number of vertical frames
 
-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;
+r.y:=(Frame mod ny) * h;
+r.x:=(Frame div ny) * w;
+r.w:=w;
+r.h:=h;
+ComputeTexcoords(Texture, @r, @TextureBuffer);
 
-glBindTexture(GL_TEXTURE_2D, Texture^.id);
+glBindTexture(GL_TEXTURE_2D, Texture^.atlas^.id);
 
 VertexBuffer[0].X:= -hw;
 VertexBuffer[0].Y:= w / -2;
@@ -188,20 +238,11 @@
 VertexBuffer[3].X:= -hw;
 VertexBuffer[3].Y:= w / 2;
 
-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]);
 glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer));
 
-glPopMatrix
+ResetModelview;
 end;
 
 procedure DrawSpriteRotated(Sprite: TSprite; X, Y, Dir: LongInt; Angle: real);
@@ -214,19 +255,18 @@
 
 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);
@@ -238,19 +278,18 @@
 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^.id);
+glBindTexture(GL_TEXTURE_2D, Texture^.atlas^.id);
 
 VertexBuffer[0].X:= -hw;
 VertexBuffer[0].Y:= -hh;
@@ -261,11 +300,11 @@
 VertexBuffer[3].X:= -hw;
 VertexBuffer[3].Y:= hh;
 
-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);
@@ -329,8 +368,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 +379,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 +392,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 +415,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 +442,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 +473,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 +541,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/uStore.pas	Mon Jun 25 10:16:19 2012 +0400
+++ b/hedgewars/uStore.pas	Mon Jun 25 10:44:27 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;
 
 //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;
@@ -625,6 +659,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 +822,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 +889,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 +900,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;
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -1033,11 +1308,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 +1422,10 @@
 
 procedure freeModule;
 begin
+{$IFDEF GL2}
+    glDeleteProgram(shaderMain);
+    glDeleteProgram(shaderWater);
+{$ENDIF}
     StoreRelease(false);
     TTF_Quit();
 {$IFDEF SDL13}
--- a/hedgewars/uTextures.pas	Mon Jun 25 10:16:19 2012 +0400
+++ b/hedgewars/uTextures.pas	Mon Jun 25 10:44:27 2012 +0200
@@ -26,6 +26,7 @@
 procedure Surface2GrayScale(surf: PSDL_Surface);
 function  Surface2Tex(surf: PSDL_Surface; enableClamp: boolean): PTexture;
 procedure FreeTexture(tex: PTexture);
+procedure ComputeTexcoords(texture: PTexture; r: PSDL_Rect; tb: PVertexRect);
 
 procedure initModule;
 procedure freeModule;
@@ -47,10 +48,44 @@
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
 end;
 
+procedure ComputeTexcoords(texture: PTexture; r: PSDL_Rect; tb: PVertexRect);
+var x0, y0, x1, y1: Real;
+    w, h, aw, ah: LongInt;
+const texelOffset = 0.0;
+begin
+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:= (r^.x +     texelOffset)/aw;
+x1:= (r^.x + w - texelOffset)/aw;
+y0:= (r^.y +     texelOffset)/ah;
+y1:= (r^.y + h - texelOffset)/ah;
+
+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;
+
 procedure ResetVertexArrays(texture: PTexture);
+var r: TSDL_Rect;
 begin
 with texture^ do
-    begin
+begin
     vb[0].X:= 0;
     vb[0].Y:= 0;
     vb[1].X:= w;
@@ -59,16 +94,13 @@
     vb[2].Y:= h;
     vb[3].X:= 0;
     vb[3].Y:= h;
+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
-    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;
@@ -84,16 +116,22 @@
     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;
 
 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);
@@ -136,20 +174,27 @@
     end;
 TextureList:= Surface2Tex;
 
+// Atlas allocation happens here later on. For now we just allocate one exclusive atlas per sprite
+new(Surface2Tex^.atlas);
+
 Surface2Tex^.w:= surf^.w;
 Surface2Tex^.h:= surf^.h;
+Surface2Tex^.x:=0;
+Surface2Tex^.y:=0;
+Surface2Tex^.isRotated:=false;
+
 
 if (surf^.format^.BytesPerPixel <> 4) then
     begin
     TryDo(false, 'Surface2Tex failed, expecting 32 bit surface', true);
-    Surface2Tex^.id:= 0;
+    Surface2Tex^.atlas^.id:= 0;
     exit
     end;
 
 
-glGenTextures(1, @Surface2Tex^.id);
+glGenTextures(1, @Surface2Tex^.atlas^.id);
 
-glBindTexture(GL_TEXTURE_2D, Surface2Tex^.id);
+glBindTexture(GL_TEXTURE_2D, Surface2Tex^.atlas^.id);
 
 if SDL_MustLock(surf) then
     SDLTry(SDL_LockSurface(surf) >= 0, true);
@@ -164,8 +209,8 @@
     tw:= toPowerOf2(Surf^.w);
     th:= toPowerOf2(Surf^.h);
 
-    Surface2Tex^.rx:= Surf^.w / tw;
-    Surface2Tex^.ry:= Surf^.h / th;
+    Surface2Tex^.atlas^.w:=tw;
+    Surface2Tex^.atlas^.h:=th;
 
     tmpp:= GetMem(tw * th * surf^.format^.BytesPerPixel);
 
@@ -195,8 +240,8 @@
     end
 else
     begin
-    Surface2Tex^.rx:= 1.0;
-    Surface2Tex^.ry:= 1.0;
+    Surface2Tex^.atlas^.w:=Surf^.w;
+    Surface2Tex^.atlas^.h:=Surf^.h;
     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf^.w, surf^.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf^.pixels);
     end;
 
@@ -214,13 +259,16 @@
 begin
 if tex <> nil then
     begin
+    // Atlas cleanup happens here later on. For now we just free as each sprite has one atlas
+    Dispose(tex^.atlas);
+
     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);
+    glDeleteTextures(1, @tex^.atlas^.id);
     Dispose(tex);
     end
 end;
@@ -236,7 +284,7 @@
     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)));
+        AddFileLog('Sprite not freed: width='+inttostr(LongInt(TextureList^.w))+' height='+inttostr(LongInt(TextureList^.h))+' priority='+inttostr(round(TextureList^.atlas^.priority*1000)));
         FreeTexture(TextureList);
         end
 end;
--- a/hedgewars/uTypes.pas	Mon Jun 25 10:16:19 2012 +0400
+++ b/hedgewars/uTypes.pas	Mon Jun 25 10:44:27 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,13 +200,30 @@
             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;
+    
     TTexture = record
-            id: GLuint;
+            atlas: PAtlas;
             w, h, scale: LongInt;
-            rx, ry: GLfloat;
-            priority: GLfloat;
-            vb, tb: array [0..3] of TVertex2f;
+
+            x, y: LongInt; // Offset in the texture atlas
+            isRotated: boolean; // if true sprite is flipped in the atlas taking w pixels along the y and h pixels along the x axis
+
+            // Cached values for texel coordinates and vertex coordinates            
+            vb, tb: TVertexRect;
+
             PrevTexture, NextTexture: PTexture;
             end;
 
--- a/hedgewars/uVariables.pas	Mon Jun 25 10:16:19 2012 +0400
+++ b/hedgewars/uVariables.pas	Mon Jun 25 10:44:27 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 = (
@@ -2427,6 +2428,7 @@
     SyncTexture,
     ConfirmTexture: PTexture;
     cScaleFactor: GLfloat;
+    cStereoDepth: GLfloat;
     SupportNPOTT: Boolean;
     Step: LongInt;
     squaresize : LongInt;
@@ -2465,6 +2467,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
@@ -2614,6 +2619,9 @@
     GrayScale:= false;
 
     LuaGoals:= '';
+
+    MatrixLoadIdentity(mModelview);
+    MatrixLoadIdentity(mProjection);
 end;
 
 procedure freeModule;
--- a/hedgewars/uWorld.pas	Mon Jun 25 10:16:19 2012 +0400
+++ b/hedgewars/uWorld.pas	Mon Jun 25 10:44:27 2012 +0200
@@ -77,7 +77,6 @@
     amSel: TAmmoType = amNothing;
     missionTex: PTexture;
     missionTimer: LongInt;
-    stereoDepth: GLfloat;
     isFirstFrame: boolean;
     AMAnimType: LongInt;
 
@@ -795,28 +794,29 @@
         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;
+    r: TSDL_Rect;
 begin
 if SuddenDeathDmg then
     sprite:= sprSDWater
@@ -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);
@@ -1075,35 +1071,17 @@
 
 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);
@@ -1556,7 +1534,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);
@@ -1823,14 +1801,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 Jun 25 10:16:19 2012 +0400
+++ b/share/hedgewars/Data/CMakeLists.txt	Mon Jun 25 10:44:27 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	Mon Jun 25 10:44:27 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	Mon Jun 25 10:44:27 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	Mon Jun 25 10:44:27 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	Mon Jun 25 10:44:27 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	Mon Jun 25 10:44:27 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;
+}