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