--- a/hedgewars/options.inc Mon Jun 25 09:40:56 2012 -0400
+++ b/hedgewars/options.inc Mon Jun 25 15:46:49 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 Mon Jun 25 15:46:49 2012 +0200
@@ -0,0 +1,698 @@
+{$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;
+
+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
+ end;
+
+var
+ Info: array[0..MaxAtlases-1] of AtlasInfo;
+
+
+////////////////////////////////////////////////////////////////////////////////
+// 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;
+
+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;
+
+ 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;
+ png_info: png_infop;
+ w, h, sz: Integer;
+ filename: string;
+ rows: array of png_bytep;
+ size: Integer;
+ i, j: Integer;
+ mem, p, pp: PByte;
+begin
+ filename:= '/home/wolfgangst/hedgewars/dump/atlas_' + IntToStrPad(DumpID) + '.png';
+ Assign(DumpFile, filename);
+ inc(DumpID);
+ Rewrite(DumpFile);
+
+ w:= info.TextureInfo.w;
+ h:= info.TextureInfo.h;
+ size:= w * h * 4;
+ SetLength(rows, h);
+ GetMem(mem, size);
+
+ glBindTexture(GL_TEXTURE_2D, info.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;
+
+ 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
+ DumpID:=0;
+ for i:= 0 to pred(MaxAtlases) do
+ Info[i].Allocated:= false;
+end;
+
+end.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hedgewars/uBinPacker.pas Mon Jun 25 15:46:49 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 Jun 25 09:40:56 2012 -0400
+++ b/hedgewars/uChat.pas Mon Jun 25 15:46:49 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 Jun 25 09:40:56 2012 -0400
+++ b/hedgewars/uGearsRender.pas Mon Jun 25 15:46:49 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 09:40:56 2012 -0400
+++ b/hedgewars/uLandTexture.pas Mon Jun 25 15:46:49 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 15:46:49 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 09:40:56 2012 -0400
+++ b/hedgewars/uRender.pas Mon Jun 25 15:46:49 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/uRenderUtils.pas Mon Jun 25 09:40:56 2012 -0400
+++ b/hedgewars/uRenderUtils.pas Mon Jun 25 15:46:49 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 Jun 25 09:40:56 2012 -0400
+++ b/hedgewars/uScript.pas Mon Jun 25 15:46:49 2012 +0200
@@ -738,8 +738,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 Jun 25 09:40:56 2012 -0400
+++ b/hedgewars/uStore.pas Mon Jun 25 15:46:49 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;
@@ -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:= Surface2Atlas(texsurf, false);
end
end;
@@ -363,12 +392,12 @@
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);
+ 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 +411,8 @@
{$ELSE}
if saveSurf then
Surface:= tmpsurf
- else
- SDL_FreeSurface(tmpsurf)
+ //else
+ // SDL_FreeSurface(tmpsurf) released by FreeTexture
{$ENDIF}
end
end
@@ -398,8 +427,7 @@
if tmpsurf = nil then
tmpsurf:= LoadImage(Pathz[ptGraphics] + '/' + cHHFileName, ifAlpha or ifCritical or ifTransparent);
-HHTexture:= Surface2Tex(tmpsurf, false);
-SDL_FreeSurface(tmpsurf);
+HHTexture:= Surface2Atlas(tmpsurf, false);
InitHealth;
@@ -419,8 +447,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 +456,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
@@ -449,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;
@@ -582,10 +608,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:= Surface2Atlas(texsurf, true);
end;
end;
@@ -625,6 +648,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 +811,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 +878,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 +889,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 +1041,10 @@
if texsurf = nil then
texsurf:= LoadImage(Pathz[ptGraphics] + '/Progress', ifCritical or ifTransparent);
- ProgrTex:= Surface2Tex(texsurf, false);
+ ProgrTex:= Surface2Atlas(texsurf, false);
squaresize:= texsurf^.w shr 1;
numsquares:= texsurf^.h div squaresize;
- SDL_FreeSurface(texsurf);
uMobile.GameLoading();
end;
@@ -926,8 +1189,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 +1295,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 +1409,10 @@
procedure freeModule;
begin
+{$IFDEF GL2}
+ glDeleteProgram(shaderMain);
+ glDeleteProgram(shaderWater);
+{$ENDIF}
StoreRelease(false);
TTF_Quit();
{$IFDEF SDL13}
--- a/hedgewars/uTextures.pas Mon Jun 25 09:40:56 2012 -0400
+++ b/hedgewars/uTextures.pas Mon Jun 25 15:46:49 2012 +0200
@@ -24,14 +24,15 @@
function NewTexture(width, height: Longword; buf: Pointer): PTexture;
procedure Surface2GrayScale(surf: PSDL_Surface);
-function Surface2Tex(surf: PSDL_Surface; enableClamp: boolean): 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;
var TextureList: PTexture;
@@ -47,10 +48,58 @@
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, 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;
+ 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
+ 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);
+var r: TSDL_Rect;
begin
with texture^ do
- begin
+begin
vb[0].X:= 0;
vb[0].Y:= 0;
vb[1].X:= w;
@@ -59,16 +108,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 +130,24 @@
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;
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 +175,50 @@
end;
-function Surface2Tex(surf: PSDL_Surface; enableClamp: boolean): PTexture;
+function Surface2Atlas(surf: PSDL_Surface; enableClamp: boolean): PTexture;
var tw, th, x, y: Longword;
tmpp: pointer;
fromP4, toP4: PLongWordArray;
begin
-new(Surface2Tex);
-Surface2Tex^.PrevTexture:= nil;
-Surface2Tex^.NextTexture:= nil;
+ 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;
if TextureList <> nil then
begin
- TextureList^.PrevTexture:= Surface2Tex;
- Surface2Tex^.NextTexture:= TextureList
+ TextureList^.PrevTexture:= Surface2Atlas;
+ Surface2Atlas^.NextTexture:= TextureList
end;
-TextureList:= Surface2Tex;
+TextureList:= Surface2Atlas;
+
+// Atlas allocation happens here later on. For now we just allocate one exclusive atlas per sprite
+new(Surface2Atlas^.atlas);
-Surface2Tex^.w:= surf^.w;
-Surface2Tex^.h:= surf^.h;
+Surface2Atlas^.w:= surf^.w;
+Surface2Atlas^.h:= surf^.h;
+Surface2Atlas^.x:=0;
+Surface2Atlas^.y:=0;
+Surface2Atlas^.isRotated:=false;
+Surface2Atlas^.surface:= surf;
+Surface2Atlas^.shared:= false;
+
if (surf^.format^.BytesPerPixel <> 4) then
begin
TryDo(false, 'Surface2Tex failed, expecting 32 bit surface', true);
- Surface2Tex^.id:= 0;
+ 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 +233,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 +264,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);
@@ -214,19 +283,33 @@
begin
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);
+
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);
+
+ if (tex^.surface <> nil) then
+ SDL_FreeSurface(tex^.surface);
Dispose(tex);
end
end;
procedure initModule;
begin
+uAtlas.initModule;
TextureList:= nil;
end;
@@ -236,7 +319,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 09:40:56 2012 -0400
+++ b/hedgewars/uTypes.pas Mon Jun 25 15:46:49 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,34 @@
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
+
+ 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;
+
PrevTexture, NextTexture: PTexture;
end;
--- a/hedgewars/uVariables.pas Mon Jun 25 09:40:56 2012 -0400
+++ b/hedgewars/uVariables.pas Mon Jun 25 15:46:49 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 09:40:56 2012 -0400
+++ b/hedgewars/uWorld.pas Mon Jun 25 15:46:49 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,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);
@@ -961,7 +957,7 @@
begin
glClear(GL_COLOR_BUFFER_BIT);
DrawWorldStereo(Lag, rmDefault)
- end
+ end;
{$IFNDEF S3D_DISABLED}
else if (cStereoMode = smAFR) then
begin
@@ -1069,41 +1065,24 @@
else
glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE);
DrawWorldStereo(Lag, rmRightEye);
- end
+ end;
{$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);
@@ -1483,8 +1462,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
@@ -1501,8 +1479,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);
@@ -1556,7 +1533,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 +1800,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 09:40:56 2012 -0400
+++ b/share/hedgewars/Data/CMakeLists.txt Mon Jun 25 15:46:49 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 15:46:49 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 15:46:49 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 15:46:49 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 15:46:49 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 15:46:49 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;
+}