using atlas for rendering now
authorWolfgang Steffens <WolfgangSteff@gmail.com>
Mon, 25 Jun 2012 15:46:08 +0200
changeset 7297 af64b509725c
parent 7295 e70b81854fb9
child 7299 c633d00dc593
using atlas for rendering now
hedgewars/uAtlas.pas
hedgewars/uStore.pas
hedgewars/uTextures.pas
hedgewars/uTypes.pas
hedgewars/uWorld.pas
--- a/hedgewars/uAtlas.pas	Mon Jun 25 12:02:54 2012 +0200
+++ b/hedgewars/uAtlas.pas	Mon Jun 25 15:46:08 2012 +0200
@@ -11,14 +11,15 @@
 
 function Surface2Tex_(surf: PSDL_Surface; enableClamp: boolean): PTexture;
 procedure FreeTexture_(sprite: PTexture);
+procedure DebugAtlas;
 
 implementation
 
-uses GLunit, uBinPacker, uDebug, png, sysutils;
+uses GLunit, uBinPacker, uDebug, png, sysutils, uTextures;
 
 const
-    MaxAtlases = 1;    // Maximum number of atlases (textures) to allocate
-    MaxTexSize = 4096; // Maximum atlas size in pixels
+    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
 
@@ -36,6 +37,40 @@
 ////////////////////////////////////////////////////////////////////////////////
 // Debug routines
 
+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
     DumpID: Integer;
     DumpFile: File of byte;
@@ -75,6 +110,57 @@
    IntToStrPad:=s;
 end;
 
+// GL1 ATLAS DEBUG ONLY CODE!
+procedure DebugAtlas;
+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);
+        glGetIntegerv(GL_CURRENT_PROGRAM, @prog);
+
+        glUseProgram(0);
+        glPushMatrix;
+        glLoadIdentity;
+        glOrtho(0, vp[2], vp[3], 0, -1, 1);
+
+
+        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;
+
+        inc(x);
+        if (x = 2) then
+        begin
+            x:=0;
+            inc(y);
+        end;
+     
+
+        glUseProgram(prog);
+    end;
+end;
+
 procedure DumpAtlas(var info: AtlasInfo);
 var
     png: png_structp;
@@ -147,6 +233,9 @@
     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);
@@ -275,6 +364,7 @@
     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);
@@ -288,7 +378,7 @@
 
     //if GrayScale then
     //    Surface2GrayScale(surf);
-    DebugColorize(surf);
+    //DebugColorize(surf);
 
     glBindTexture(GL_TEXTURE_2D, info.TextureInfo.id);
     if (sp^.isRotated) then
@@ -306,9 +396,15 @@
 
     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; newSprite: PTexture; surf: PSDL_Surface);
+procedure Repack(var info: AtlasInfo; newAtlas: Atlas);
 var
     base: PByte;
     oldSize: Integer;
@@ -373,8 +469,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 // Sprite allocation logic
 
-function TryRepack(var info: AtlasInfo; w, h: Integer; hasNewSprite: boolean; 
-                   newSprite: Size; surf: PSDL_Surface): boolean;
+function TryRepack(var info: AtlasInfo; w, h: Integer; hasNewSprite: boolean; newSprite: Size): boolean;
 var
     sizes: SizeList;
     repackedAtlas: Atlas;
@@ -402,16 +497,12 @@
     if atlasInsertSet(repackedAtlas, sizes, rects) then
     begin
         TryRepack:= true;
-        if hasNewSprite then
-            sprite:= PTexture(newSprite.UserData)
-        else
-            sprite:= nil;
-        Repack(info, repackedAtlas, sprite, surf);
+        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);
+        //DumpAtlas(info);
         exit;
     end;
 
@@ -431,7 +522,7 @@
     begin
         // we succeeded adaptivley allocating the sprite to the i'th atlas.
         Upload(info, rect, surf);
-        DumpAtlas(info);
+        //DumpAtlas(info);
         TryInsert:= true;
     end;
 end;
@@ -460,6 +551,7 @@
     sprite^.y:= 0;
     sprite^.isRotated:= false;
     sprite^.surface:= surf;
+    sprite^.shared:= true;
 
     sz:= SizeForSprite(sprite);
 
@@ -470,7 +562,7 @@
         if not Info[i].Allocated then
             continue;
         if TryInsert(Info[i], sz, surf) then
-            exit; 
+            exit;
     end;
 
 
@@ -481,7 +573,7 @@
         if not Info[i].Allocated then
             continue;
 
-        if TryRepack(Info[i], Info[i].PackerInfo.width, Info[i].PackerInfo.height, true, sz, surf) then
+        if TryRepack(Info[i], Info[i].PackerInfo.width, Info[i].PackerInfo.height, true, sz) then
             exit;
     end;
 
@@ -498,7 +590,7 @@
         EnlargeSize(currentWidth, currentHeight);
         while (currentWidth <= MaxTexSize) and (currentHeight <= MaxTexSize) do
         begin
-            if TryRepack(Info[i], currentWidth, currentHeight, true, sz, surf) then
+            if TryRepack(Info[i], currentWidth, currentHeight, true, sz) then
                 exit;
             EnlargeSize(currentWidth, currentHeight);
         end;
@@ -555,6 +647,7 @@
     if sprite = nil then
         exit;
 
+    deleteAt:= -1;
     for i:= 0 to pred(MaxAtlases) do
     begin
         if sprite^.atlas <> @Info[i].TextureInfo then
@@ -570,7 +663,7 @@
                 inc(usedArea, r.width * r.height);
         end;
 
-        rectangleListRemoveAt(Info[i].PackerInfo.usedRectangles, j);
+        rectangleListRemoveAt(Info[i].PackerInfo.usedRectangles, deleteAt);
         dispose(sprite);
 
         while true do
@@ -586,8 +679,10 @@
 
             CompactSize(atlasW, atlasH);
             unused:= unused;
-            TryRepack(Info[i], atlasW, atlasH, false, unused, nil);
+            TryRepack(Info[i], atlasW, atlasH, false, unused);
         end;
+
+        exit;
     end;
 end;
 
--- a/hedgewars/uStore.pas	Mon Jun 25 12:02:54 2012 +0200
+++ b/hedgewars/uStore.pas	Mon Jun 25 15:46:08 2012 +0200
@@ -475,7 +475,7 @@
     SpritesData[ii].Texture:= nil;
     if (SpritesData[ii].Surface <> nil) and (not reload) then
         begin
-        SDL_FreeSurface(SpritesData[ii].Surface);
+        //SDL_FreeSurface(SpritesData[ii].Surface); released by FreeTexture
         SpritesData[ii].Surface:= nil
         end
     end;
--- a/hedgewars/uTextures.pas	Mon Jun 25 12:02:54 2012 +0200
+++ b/hedgewars/uTextures.pas	Mon Jun 25 15:46:08 2012 +0200
@@ -49,12 +49,13 @@
 end;
 
 procedure ComputeTexcoords(texture: PTexture; r: PSDL_Rect; tb: PVertexRect);
-var x0, y0, x1, y1: Real;
+var x0, y0, x1, y1, tmp: 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;
@@ -66,19 +67,32 @@
     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;
+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;
 
-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
+if (texture^.isRotated) then
+begin
+  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;
 
 procedure ResetVertexArrays(texture: PTexture);
@@ -126,6 +140,8 @@
 NewTexture^.w:=width;
 NewTexture^.h:=height;
 NewTexture^.isRotated:=false;
+NewTexture^.shared:=false;
+NewTexture^.surface:=nil;
 
 ResetVertexArrays(NewTexture);
 
@@ -164,8 +180,12 @@
     tmpp: pointer;
     fromP4, toP4: PLongWordArray;
 begin
-    if (surf^.w <= 128) and (surf^.h <= 128) then
-        Surface2Tex_(surf, enableClamp); // run the atlas side by side for debugging
+    if (surf^.w <= 256) and (surf^.h <= 256) then
+    begin
+        Surface2Atlas:= Surface2Tex_(surf, enableClamp); // run the atlas side by side for debugging
+        ResetVertexArrays(Surface2Atlas);
+        exit;
+    end;
 new(Surface2Atlas);
 Surface2Atlas^.PrevTexture:= nil;
 Surface2Atlas^.NextTexture:= nil;
@@ -185,6 +205,7 @@
 Surface2Atlas^.y:=0;
 Surface2Atlas^.isRotated:=false;
 Surface2Atlas^.surface:= surf;
+Surface2Atlas^.shared:= false;
 
 
 if (surf^.format^.BytesPerPixel <> 4) then
@@ -260,9 +281,15 @@
 // if nil is passed nothing is done
 procedure FreeTexture(tex: PTexture);
 begin
-    FreeTexture_(tex); // run atlas side by side for debugging
 if tex <> nil then
     begin
+        if tex^.shared then
+        begin
+            FreeTexture_(tex); // run atlas side by side for debugging
+            SDL_FreeSurface(tex^.surface);
+            exit;
+        end;
+
     // Atlas cleanup happens here later on. For now we just free as each sprite has one atlas
     Dispose(tex^.atlas);
 
@@ -274,7 +301,8 @@
         TextureList:= tex^.NextTexture;
     glDeleteTextures(1, @tex^.atlas^.id);
 
-    SDL_FreeSurface(tex^.surface);
+    if (tex^.surface <> nil) then
+        SDL_FreeSurface(tex^.surface);
     Dispose(tex);
     end
 end;
--- a/hedgewars/uTypes.pas	Mon Jun 25 12:02:54 2012 +0200
+++ b/hedgewars/uTypes.pas	Mon Jun 25 15:46:08 2012 +0200
@@ -221,6 +221,8 @@
             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
 
+            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            
--- a/hedgewars/uWorld.pas	Mon Jun 25 12:02:54 2012 +0200
+++ b/hedgewars/uWorld.pas	Mon Jun 25 15:46:08 2012 +0200
@@ -60,7 +60,8 @@
     uCaptions,
     uCursor,
     uCommands,
-    uMobile
+    uMobile,
+    uAtlas
     ;
 
 var cWaveWidth, cWaveHeight: LongInt;
@@ -956,7 +957,7 @@
         begin
         glClear(GL_COLOR_BUFFER_BIT);
         DrawWorldStereo(Lag, rmDefault)
-        end
+        end;
 {$IFNDEF S3D_DISABLED}
     else if (cStereoMode = smAFR) then
         begin
@@ -1064,8 +1065,9 @@
         else
             glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE);
         DrawWorldStereo(Lag, rmRightEye);
-        end
+        end;
 {$ENDIF}
+    DebugAtlas;
 end;
 
 procedure ChangeDepth(rm: TRenderMode; d: GLfloat);