# HG changeset patch # User Wolfgang Steffens # Date 1340632009 -7200 # Node ID c633d00dc593d34a363fc1bbb49d4da43a479c8f # Parent af64b509725cfbac7ad87116b7b052594915bf6b# Parent 390d76b29ed0e6300630c297beef3fd480c6d423 Merge diff -r 390d76b29ed0 -r c633d00dc593 hedgewars/hwengine.pas diff -r 390d76b29ed0 -r c633d00dc593 hedgewars/options.inc --- a/hedgewars/options.inc Mon Jun 25 09:40:56 2012 -0400 +++ b/hedgewars/options.inc Mon Jun 25 15:46:49 2012 +0200 @@ -30,6 +30,8 @@ {$DEFINE USE_LUA_SCRIPT} +{$DEFINE GL2} + {$IFDEF ANDROID} {$DEFINE MOBILE} {$DEFINE USE_SDLTHREADS} @@ -53,6 +55,10 @@ {$ENDIF} +{$IFDEF GL2} + {$DEFINE S3D_DISABLED} +{$ENDIF} + {$IFDEF WIN32} {$DEFINE USE_CONTEXT_RESTORE} {$ENDIF} diff -r 390d76b29ed0 -r c633d00dc593 hedgewars/uAtlas.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uAtlas.pas Mon Jun 25 15:46:49 2012 +0200 @@ -0,0 +1,698 @@ +{$INCLUDE "options.inc"} +{$IF GLunit = GL}{$DEFINE GLunit:=GL,GLext}{$ENDIF} + +unit uAtlas; + +interface + +uses SDLh, uTypes; + +procedure initModule; + +function Surface2Tex_(surf: PSDL_Surface; enableClamp: boolean): PTexture; +procedure FreeTexture_(sprite: PTexture); +procedure DebugAtlas; + +implementation + +uses GLunit, uBinPacker, uDebug, png, sysutils, uTextures; + +const + MaxAtlases = 4; // Maximum number of atlases (textures) to allocate + MaxTexSize = 1024; // Maximum atlas size in pixels + MinTexSize = 128; // Minimum atlas size in pixels + CompressionThreshold = 0.4; // Try to compact (half the size of) an atlas, when occupancy is less than this + +type + AtlasInfo = record + PackerInfo: Atlas; // Rectangle packer context + TextureInfo: TAtlas; // OpenGL texture information + Allocated: boolean; // indicates if this atlas is in use + end; + +var + Info: array[0..MaxAtlases-1] of AtlasInfo; + + +//////////////////////////////////////////////////////////////////////////////// +// Debug routines + +procedure AssertCount(tex: PTexture; count: Integer); +var + i, j: Integer; + found: Integer; +begin + found:= 0; + for i:= 0 to pred(MaxAtlases) do + begin + if not Info[i].Allocated then + continue; + for j:=0 to pred(Info[i].PackerInfo.usedRectangles.count) do + begin + if Info[i].PackerInfo.usedRectangles.data[j].UserData = tex then + inc(found); + end; + end; + if found <> count then + begin + writeln('AssertCount(', IntToHex(Integer(tex), 8), ') failed, found ', found, ' times'); + + for i:= 0 to pred(MaxAtlases) do + begin + if not Info[i].Allocated then + continue; + for j:=0 to pred(Info[i].PackerInfo.usedRectangles.count) do + begin + if Info[i].PackerInfo.usedRectangles.data[j].UserData = tex then + writeln(' found in atlas ', i, ' at slot ', j); + end; + end; + halt(-2); + end; +end; + +var + DumpID: Integer; + DumpFile: File of byte; + +const + PNG_COLOR_TYPE_RGBA = 6; + PNG_COLOR_TYPE_RGB = 2; + PNG_INTERLACE_NONE = 0; + PNG_COMPRESSION_TYPE_DEFAULT = 0; + PNG_FILTER_TYPE_DEFAULT = 0; + + + +procedure writefunc(png: png_structp; buffer: png_bytep; size: QWord); cdecl; +var + p: Pbyte; + i: Integer; +begin + //TStream(png_get_io_ptr(png)).Write(buffer^, size); + BlockWrite(DumpFile, buffer^, size); +{ p:= PByte(buffer^); + for i:=0 to pred(size) do + begin + Write(DumpFile, p^); + inc(p); + end;} +end; + +function IntToStrPad(i: Integer): string; +var + s: string; +begin + s:= IntToStr(i); + if (i < 10) then s:='0' + s; + if (i < 100) then s:='0' + s; + + IntToStrPad:=s; +end; + +// GL1 ATLAS DEBUG ONLY CODE! +procedure DebugAtlas; +var + vp: array[0..3] of GLint; + prog: GLint; + i: Integer; + x, y: Integer; +const + SZ = 512; +begin + x:= 0; + y:= 0; + for i:= 0 to pred(MaxAtlases) do + begin + if not Info[i].allocated then + continue; + glGetIntegerv(GL_VIEWPORT, @vp); + glGetIntegerv(GL_CURRENT_PROGRAM, @prog); + + glUseProgram(0); + glPushMatrix; + glLoadIdentity; + glOrtho(0, vp[2], vp[3], 0, -1, 1); + + + glBindTexture(GL_TEXTURE_2D, Info[i].TextureInfo.id); + glBegin(GL_QUADS); + glTexCoord2f(0.0, 0.0); + glVertex2i(x * SZ, y * SZ); + glTexCoord2f(1.0, 0.0); + glVertex2i((x + 1) * SZ, y * SZ); + glTexCoord2f(1.0, 1.0); + glVertex2i((x + 1) * SZ, (y + 1) * SZ); + glTexCoord2f(0.0, 1.0); + glVertex2i(x * SZ, (y + 1) * SZ); + glEnd(); + + glPopMatrix; + + inc(x); + if (x = 2) then + begin + x:=0; + inc(y); + end; + + + glUseProgram(prog); + end; +end; + +procedure DumpAtlas(var info: AtlasInfo); +var + png: png_structp; + png_info: png_infop; + w, h, sz: Integer; + filename: string; + rows: array of png_bytep; + size: Integer; + i, j: Integer; + mem, p, pp: PByte; +begin + filename:= '/home/wolfgangst/hedgewars/dump/atlas_' + IntToStrPad(DumpID) + '.png'; + Assign(DumpFile, filename); + inc(DumpID); + Rewrite(DumpFile); + + w:= info.TextureInfo.w; + h:= info.TextureInfo.h; + size:= w * h * 4; + SetLength(rows, h); + GetMem(mem, size); + + glBindTexture(GL_TEXTURE_2D, info.TextureInfo.id); + + glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, mem); + + p:= mem; + for i:= 0 to pred(h) do + begin + rows[i]:= p; + pp:= p; + inc(pp, 3); + {for j:= 0 to pred(w) do + begin + pp^:=255; + inc(pp, 4); + end;} + inc(p, w * 4); + end; + + png := png_create_write_struct(PNG_LIBPNG_VER_STRING, nil, nil, nil); + png_info := png_create_info_struct(png); + + png_set_write_fn(png, nil, @writefunc, nil); + png_set_IHDR(png, png_info, w, h, 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + png_write_info(png, png_info); + png_write_image(png, @rows[0]); + png_write_end(png, png_info); + png_destroy_write_struct(@png, @png_info); + + FreeMem(mem); + + SetLength(rows, 0); + Close(DumpFile); + + //if (DumpID >= 30) then + // halt(0); +end; + +//////////////////////////////////////////////////////////////////////////////// +// Upload routines + +function createTexture(width, height: Integer): TAtlas; +var + nullTex: Pointer; +begin + createTexture.w:= width; + createTexture.h:= height; + createTexture.priority:= 0; + glGenTextures(1, @createTexture.id); + glBindTexture(GL_TEXTURE_2D, createTexture.id); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil); + + GetMem(NullTex, width * height * 4); + FillChar(NullTex^, width * height * 4, 0); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NullTex); + FreeMem(NullTex); + + glBindTexture(GL_TEXTURE_2D, 0); +end; + +function Min(x, y: Single): Single; +begin + if x < y then + Min:=x + else Min:=y; +end; + +function Max(x, y: Single): Single; +begin + if x > y then + Max:=x + else Max:=y; +end; + + +procedure HSVToRGB(const H, S, V: Single; out R, G, B: Single); +const + SectionSize = 60/360; +var + Section: Single; + SectionIndex: Integer; + f: single; + p, q, t: Single; +begin + if H < 0 then + begin + R:= V; + G:= R; + B:= R; + end + else + begin + Section:= H/SectionSize; + SectionIndex:= Trunc(Section); + f:= Section - SectionIndex; + p:= V * ( 1 - S ); + q:= V * ( 1 - S * f ); + t:= V * ( 1 - S * ( 1 - f ) ); + case SectionIndex of + 0: + begin + R:= V; + G:= t; + B:= p; + end; + 1: + begin + R:= q; + G:= V; + B:= p; + end; + 2: + begin + R:= p; + G:= V; + B:= t; + end; + 3: + begin + R:= p; + G:= q; + B:= V; + end; + 4: + begin + R:= t; + G:= p; + B:= V; + end; + else + R:= V; + G:= p; + B:= q; + end; + end; +end; + +procedure DebugColorize(surf: PSDL_Surface); +var + sz: Integer; + p: PByte; + i: Integer; + r, g, b, a, inva: Integer; + randr, randg, randb: Single; + randh: Single; +begin + sz:= surf^.w * surf^.h; + p:= surf^.pixels; + //randr:=Random; + //randg:=Random; + //randb:=1 - min(randr, randg); + randh:=Random; + HSVToRGB(randh, 1.0, 1.0, randr, randg, randb); + for i:=0 to pred(sz) do + begin + a:= p[3]; + inva:= 255 - a; + + r:=Trunc(inva*randr + p[0]*a/255); + g:=Trunc(inva*randg + p[1]*a/255); + b:=Trunc(inva*randb + p[2]*a/255); + if r > 255 then r:= 255; + if g > 255 then g:= 255; + if b > 255 then b:= 255; + + p[0]:=r; + p[1]:=g; + p[2]:=b; + p[3]:=255; + inc(p, 4); + end; +end; + +procedure Upload(var info: AtlasInfo; sprite: Rectangle; surf: PSDL_Surface); +var + sp: PTexture; + i, j, stride: Integer; + scanline: PByte; + r: TSDL_Rect; +begin + writeln('Uploading sprite to ', sprite.x, ',', sprite.y, ',', sprite.width, ',', sprite.height); + sp:= PTexture(sprite.UserData); + sp^.x:= sprite.x; + sp^.y:= sprite.y; + sp^.isRotated:= sp^.w <> sprite.width; + sp^.atlas:= @info.TextureInfo; + + if SDL_MustLock(surf) then + SDLTry(SDL_LockSurface(surf) >= 0, true); + + //if GrayScale then + // Surface2GrayScale(surf); + //DebugColorize(surf); + + glBindTexture(GL_TEXTURE_2D, info.TextureInfo.id); + if (sp^.isRotated) then + begin + scanline:= surf^.pixels; + for i:= 0 to pred(sprite.width) do + begin + glTexSubImage2D(GL_TEXTURE_2D, 0, sprite.x + i, sprite.y, 1, sprite.height, GL_RGBA, GL_UNSIGNED_BYTE, scanline); + inc(scanline, sprite.height * 4); + end; + end + else + glTexSubImage2D(GL_TEXTURE_2D, 0, sprite.x, sprite.y, sprite.width, sprite.height, GL_RGBA, GL_UNSIGNED_BYTE, surf^.pixels); + glBindTexture(GL_TEXTURE_2D, 0); + + if SDL_MustLock(surf) then + SDL_UnlockSurface(surf); + + r.x:= 0; + r.y:= 0; + r.w:= sp^.w; + r.h:= sp^.h; + ComputeTexcoords(sp, @r, @sp^.tb); +end; + +procedure Repack(var info: AtlasInfo; newAtlas: Atlas); +var + base: PByte; + oldSize: Integer; + oldWidth: Integer; + offset: Integer; + i,j : Integer; + r: Rectangle; + sp: PTexture; + newIsRotated: boolean; + newSpriteRect: Rectangle; +begin + writeln('Repacking atlas (', info.PackerInfo.width, 'x', info.PackerInfo.height, ')', ' -> (', newAtlas.width, 'x', newAtlas.height, ')'); + + // delete the old atlas + glDeleteTextures(1, @info.TextureInfo.id); + + // create a new atlas with different size + info.TextureInfo:= createTexture(newAtlas.width, newAtlas.height); + glBindTexture(GL_TEXTURE_2D, info.TextureInfo.id); + + atlasDelete(info.PackerInfo); + info.PackerInfo:= newAtlas; + + // and process all sprites of the new atlas + for i:=0 to pred(newAtlas.usedRectangles.count) do + begin + r:= newAtlas.usedRectangles.data[i]; + sp:= PTexture(r.UserData); + Upload(info, r, sp^.surface); + end; + + glBindTexture(GL_TEXTURE_2D, 0); +end; + + +//////////////////////////////////////////////////////////////////////////////// +// Utility functions + +function SizeForSprite(sprite: PTexture): Size; +begin + SizeForSprite.width:= sprite^.w; + SizeForSprite.height:= sprite^.h; + SizeForSprite.UserData:= sprite; +end; + +procedure EnlargeSize(var x: Integer; var y: Integer); +begin + if (y < x) then + y:= y + y + else + x:= x + x; +end; + +procedure CompactSize(var x: Integer; var y: Integer); +begin + if (x > y) then + x:= x div 2 + else + y:= y div 2; +end; + +//////////////////////////////////////////////////////////////////////////////// +// Sprite allocation logic + +function TryRepack(var info: AtlasInfo; w, h: Integer; hasNewSprite: boolean; newSprite: Size): boolean; +var + sizes: SizeList; + repackedAtlas: Atlas; + sprite: PTexture; + i: Integer; + rects: RectangleList; // we wont really need this as we do a full repack using the atlas later on +begin + TryRepack:= false; + + // STEP 1: collect sizes of all existing sprites + sizeListInit(sizes); + for i:= 0 to pred(info.PackerInfo.usedRectangles.count) do + begin + sprite:= PTexture(info.PackerInfo.usedRectangles.data[i].UserData); + sizeListAdd(sizes, SizeForSprite(sprite)); + end; + + // STEP 2: add the new sprite to the list + if hasNewSprite then + sizeListAdd(sizes, newSprite); + + // STEP 3: try to create a non adaptive re-packing using the whole list + repackedAtlas:= atlasNew(w, h); + rectangleListInit(rects); + if atlasInsertSet(repackedAtlas, sizes, rects) then + begin + TryRepack:= true; + Repack(info, repackedAtlas); + // repack assigns repackedAtlas to the current info and deletes the old one + // thus we wont do atlasDelete(repackedAtlas); here + rectangleListClear(rects); + sizeListClear(sizes); + //DumpAtlas(info); + exit; + end; + + rectangleListClear(rects); + sizeListClear(sizes); + atlasDelete(repackedAtlas); +end; + +function TryInsert(var info: AtlasInfo; newSprite: Size; surf: PSDL_Surface): boolean; +var + rect: Rectangle; + sprite: PTexture; +begin + TryInsert:= false; + + if atlasInsertAdaptive(info.PackerInfo, newSprite, rect) then + begin + // we succeeded adaptivley allocating the sprite to the i'th atlas. + Upload(info, rect, surf); + //DumpAtlas(info); + TryInsert:= true; + end; +end; + +function Surface2Tex_(surf: PSDL_Surface; enableClamp: boolean): PTexture; +var + sz: Size; + sprite: PTexture; + currentWidth, currentHeight: Integer; + i: Integer; +begin + if (surf^.w > MaxTexSize) or (surf^.h > MaxTexSize) then + begin + // we could at best downscale the sprite, abort for now + writeln('Sprite size larger than maximum texture size'); + halt(-1); + end; + + // allocate the sprite + new(sprite); + Surface2Tex_:= sprite; + + sprite^.w:= surf^.w; + sprite^.h:= surf^.h; + sprite^.x:= 0; + sprite^.y:= 0; + sprite^.isRotated:= false; + sprite^.surface:= surf; + sprite^.shared:= true; + + sz:= SizeForSprite(sprite); + + // STEP 1 + // try to allocate the new sprite in one of the existing atlases + for i:= 0 to pred(MaxAtlases) do + begin + if not Info[i].Allocated then + continue; + if TryInsert(Info[i], sz, surf) then + exit; + end; + + + // STEP 2 + // none of the atlases has space left for the allocation, try a garbage collection + for i:= 0 to pred(MaxAtlases) do + begin + if not Info[i].Allocated then + continue; + + if TryRepack(Info[i], Info[i].PackerInfo.width, Info[i].PackerInfo.height, true, sz) then + exit; + end; + + // STEP 3 + // none of the atlases could be repacked in a way to fit the new sprite, try enlarging + for i:= 0 to pred(MaxAtlases) do + begin + if not Info[i].Allocated then + continue; + + currentWidth:= Info[i].PackerInfo.width; + currentHeight:= Info[i].PackerInfo.height; + + EnlargeSize(currentWidth, currentHeight); + while (currentWidth <= MaxTexSize) and (currentHeight <= MaxTexSize) do + begin + if TryRepack(Info[i], currentWidth, currentHeight, true, sz) then + exit; + EnlargeSize(currentWidth, currentHeight); + end; + end; + + // STEP 4 + // none of the existing atlases could be resized, try to allocate a new atlas + for i:= 0 to pred(MaxAtlases) do + begin + if Info[i].Allocated then + continue; + + currentWidth:= MinTexSize; + currentHeight:= MinTexSize; + while (sz.width > currentWidth) do + currentWidth:= currentWidth + currentWidth; + while (sz.height > currentHeight) do + currentHeight:= currentHeight + currentHeight; + + with Info[i] do + begin + PackerInfo:= atlasNew(currentWidth, currentHeight); + TextureInfo:= createTexture(currentWidth, currentHeight); + Allocated:= true; + end; + + if TryInsert(Info[i], sz, surf) then + exit; + + // this shouldnt have happened, the rectpacker should be able to fit the sprite + // into an unused rectangle that is the same size or larger than the requested sprite. + writeln('Internal error: atlas allocation failed'); + halt(-1); + end; + + // we reached the upperbound of resources we are willing to allocate + writeln('Exhausted maximum sprite allocation size'); + halt(-1); +end; + +//////////////////////////////////////////////////////////////////////////////// +// Sprite deallocation logic + + +procedure FreeTexture_(sprite: PTexture); +var + i, j, deleteAt: Integer; + usedArea: Integer; + totalArea: Integer; + r: Rectangle; + atlasW, atlasH: Integer; + unused: Size; +begin + if sprite = nil then + exit; + + deleteAt:= -1; + for i:= 0 to pred(MaxAtlases) do + begin + if sprite^.atlas <> @Info[i].TextureInfo then + continue; + + usedArea:= 0; + for j:=0 to pred(Info[i].PackerInfo.usedRectangles.count) do + begin + r:= Info[i].PackerInfo.usedRectangles.data[j]; + if r.UserData = sprite then + deleteAt:= j + else + inc(usedArea, r.width * r.height); + end; + + rectangleListRemoveAt(Info[i].PackerInfo.usedRectangles, deleteAt); + dispose(sprite); + + while true do + begin + atlasW:= Info[i].PackerInfo.width; + atlasH:= Info[i].PackerInfo.height; + totalArea:= atlasW * atlasH; + if usedArea >= totalArea * CompressionThreshold then + exit; + + if (atlasW = MinTexSize) and (atlasH = MinTexSize) then + exit; // we could try to move everything from this to another atlas here + + CompactSize(atlasW, atlasH); + unused:= unused; + TryRepack(Info[i], atlasW, atlasH, false, unused); + end; + + exit; + end; +end; + +procedure initModule; +var + i: Integer; +begin + DumpID:=0; + for i:= 0 to pred(MaxAtlases) do + Info[i].Allocated:= false; +end; + +end. diff -r 390d76b29ed0 -r c633d00dc593 hedgewars/uBinPacker.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uBinPacker.pas Mon Jun 25 15:46:49 2012 +0200 @@ -0,0 +1,438 @@ +unit uBinPacker; + +interface + +// implements a maxrects packer with best short side fit heuristic + +type Rectangle = record + x, y, width, height: LongInt; + UserData: Pointer; +end; + +type Size = record + width, height: LongInt; + UserData: Pointer; +end; + +type PRectangle = ^Rectangle; +type PSize = ^Size; + +type RectangleList = record + data: PRectangle; + count: LongInt; + size: LongInt; +end; + +type SizeList = record + data: PSize; + count: LongInt; + size: LongInt; +end; + +type Atlas = record + width, height: Longint; + freeRectangles: RectangleList; + usedRectangles: RectangleList; +end; + +function atlasInsertAdaptive(var a: Atlas; sz: Size; var output: Rectangle): boolean; +function atlasInsertSet(var a: Atlas; var input: SizeList; var outputs: RectangleList): boolean; +function atlasNew(width, height: LongInt): Atlas; +procedure atlasDelete(var a: Atlas); +procedure atlasReset(var a: Atlas); + +procedure rectangleListInit(var list: RectangleList); +procedure rectangleListRemoveAt(var list: RectangleList; index: LongInt); +procedure rectangleListAdd(var list: RectangleList; r: Rectangle); +procedure rectangleListClear(var list: RectangleList); +procedure sizeListInit(var list: SizeList); +procedure sizeListRemoveAt(var list: SizeList; index: LongInt); +procedure sizeListAdd(var list: SizeList; s: Size); overload; +procedure sizeListAdd(var list: SizeList; width, height: LongInt; UserData: Pointer); overload; +procedure sizeListClear(var list: SizeList); + +implementation + +uses Math; // for min/max + +procedure rectangleListRemoveAt(var list: RectangleList; index: LongInt); +var + i: Integer; +begin + i:=index; + while (i + 1 < list.count) do + begin + list.data[i]:=list.data[i + 1]; + inc(i); + end; + dec(list.count); +end; + +procedure rectangleListAdd(var list: RectangleList; r: Rectangle); +begin + if list.count >= list.size then + begin + inc(list.size, 512); + ReAllocMem(list.data, sizeof(Rectangle) * list.size); + end; + list.data[list.count]:=r; + inc(list.count); +end; + +procedure rectangleListInit(var list: RectangleList); +begin + list.data:= nil; + list.count:= 0; + list.size:= 0; +end; + +procedure rectangleListClear(var list: RectangleList); +begin + FreeMem(list.data); + list.count:= 0; + list.size:= 0; +end; + +procedure sizeListRemoveAt(var list: SizeList; index: LongInt); +begin + list.data[index]:= list.data[list.count - 1]; + dec(list.count); +end; + +procedure sizeListAdd(var list: SizeList; s: Size); overload; +begin + if list.count >= list.size then + begin + inc(list.size, 512); + ReAllocMem(list.data, sizeof(Size) * list.size); + end; + list.data[list.count]:=s; + inc(list.count); +end; + +procedure sizeListAdd(var list: SizeList; width, height: LongInt; UserData: Pointer); overload; +var + sz: Size; +begin + sz.width:= width; + sz.height:= height; + sz.UserData:= UserData; + sizeListAdd(list, sz); +end; + +procedure sizeListInit(var list: SizeList); +begin + list.data:= nil; + list.count:= 0; + list.size:= 0; +end; + +procedure sizeListClear(var list: SizeList); +begin + FreeMem(list.data); + list.count:= 0; + list.size:= 0; +end; + + +function isContainedIn(a, b: Rectangle): boolean; +begin + isContainedIn:= (a.x >= b.x) and (a.y >= b.y) + and (a.x + a.width <= b.x + b.width) + and (a.y + a.height <= b.y + b.height); +end; + +function findPositionForNewNodeBestShortSideFit(var list: RectangleList; width, height: LongInt; + var bestShortSideFit, bestLongSideFit: LongInt): Rectangle; +var + bestNode: Rectangle; + i: Integer; + ri: Rectangle; + leftoverHoriz, leftoverVert, shortSideFit, longSideFit: Longint; +begin + bestNode.x:= 0; + bestNode.y:= 0; + bestNode.width:= 0; + bestNode.height:= 0; + bestShortSideFit:= $7FFFFFFF; + + for i:=0 to pred(list.count) do + begin + ri:= list.data[i]; + + // Try to place the rectangle in upright (non-flipped) orientation. + if (ri.width >= width) and (ri.height >= height) then + begin + leftoverHoriz:= Abs(ri.width - width); + leftoverVert:= Abs(ri.height - height); + shortSideFit:= Min(leftoverHoriz, leftoverVert); + longSideFit:= Max(leftoverHoriz, leftoverVert); + + if (shortSideFit < bestShortSideFit) or + ((shortSideFit = bestShortSideFit) and (longSideFit < bestLongSideFit)) then + begin + bestNode.x:= ri.x; + bestNode.y:= ri.y; + bestNode.width:= width; + bestNode.height:= height; + bestShortSideFit:= shortSideFit; + bestLongSideFit:= longSideFit; + end; + end; + + if (ri.width >= height) and (ri.height >= width) then + begin + leftoverHoriz:= Abs(ri.width - height); + leftoverVert:= Abs(ri.height - width); + shortSideFit:= Min(leftoverHoriz, leftoverVert); + longSideFit:= Max(leftoverHoriz, leftoverVert); + + if (shortSideFit < bestShortSideFit) or + ((shortSideFit = bestShortSideFit) and (longSideFit < bestLongSideFit)) then + begin + bestNode.x:= ri.x; + bestNode.y:= ri.y; + bestNode.width:= height; + bestNode.height:= width; + bestShortSideFit:= shortSideFit; + bestLongSideFit:= longSideFit; + end; + end; + end; + + findPositionForNewNodeBestShortSideFit:= bestNode; +end; + +function scoreRect(var list: RectangleList; width, height: LongInt; var score1, score2: LongInt): Rectangle; +var + newNode: Rectangle; +begin + newNode:= findPositionForNewNodeBestShortSideFit(list, width, height, score1, score2); + + // Cannot fit the current rectangle. + if newNode.height = 0 then + begin + score1:= $7FFFFFFF; + score2:= $7FFFFFFF; + end; + + scoreRect:= newNode; +end; + +function splitFreeNode(var freeRectangles: RectangleList; freeNode, usedNode: Rectangle): boolean; +var + newNode: Rectangle; +begin + // Test with SAT if the rectangles even intersect. + if (usedNode.x >= freeNode.x + freeNode.width) or (usedNode.x + usedNode.width <= freeNode.x) or + (usedNode.y >= freeNode.y + freeNode.height) or (usedNode.y + usedNode.height <= freeNode.y) then + begin + splitFreeNode:=false; + exit; + end; + + if (usedNode.x < freeNode.x + freeNode.width) and (usedNode.x + usedNode.width > freeNode.x) then + begin + // New node at the top side of the used node. + if (usedNode.y > freeNode.y) and (usedNode.y < freeNode.y + freeNode.height) then + begin + newNode:= freeNode; + newNode.height:= usedNode.y - newNode.y; + rectangleListAdd(freeRectangles, newNode); + end; + + // New node at the bottom side of the used node. + if (usedNode.y + usedNode.height < freeNode.y + freeNode.height) then + begin + newNode:= freeNode; + newNode.y:= usedNode.y + usedNode.height; + newNode.height:= freeNode.y + freeNode.height - (usedNode.y + usedNode.height); + rectangleListAdd(freeRectangles, newNode); + end; + end; + + if (usedNode.y < freeNode.y + freeNode.height) and (usedNode.y + usedNode.height > freeNode.y) then + begin + // New node at the left side of the used node. + if (usedNode.x > freeNode.x) and (usedNode.y < freeNode.y + freeNode.width) then + begin + newNode:= freeNode; + newNode.width:= usedNode.x - newNode.x; + rectangleListAdd(freeRectangles, newNode); + end; + + // New node at the right side of the used node. + if (usedNode.x + usedNode.width < freeNode.x + freeNode.width) then + begin + newNode:= freeNode; + newNode.x:= usedNode.x + usedNode.width; + newNode.width:= freeNode.x + freeNode.width - (usedNode.x + usedNode.width); + rectangleListAdd(freeRectangles, newNode); + end; + end; + + splitFreeNode:= true; +end; + +procedure pruneFreeList(var freeRectangles: RectangleList); +var + i, j: LongInt; +begin + // Go through each pair and remove any rectangle that is redundant. + i:= 0; + while i < freeRectangles.count do + begin + j:= i + 1; + while j < freeRectangles.count do + begin + if (isContainedIn(freeRectangles.data[i], freeRectangles.data[j])) then + begin + rectangleListRemoveAt(freeRectangles, i); + dec(i); + break; + end; + + if (isContainedIn(freeRectangles.data[j], freeRectangles.data[i])) then + rectangleListRemoveAt(freeRectangles, j) + else + inc(j); + end; + inc(i); + end; +end; + +function atlasInsertAdaptive(var a: Atlas; sz: Size; var output: Rectangle): boolean; +var + newNode: Rectangle; + score1, score2: LongInt; + numRectanglesToProcess: LongInt; + i: LongInt; +begin + newNode:= findPositionForNewNodeBestShortSideFit(a.freeRectangles, sz.width, sz.height, score1, score2); + if newNode.height = 0 then + begin + output:= newNode; + output.UserData:= nil; + atlasInsertAdaptive:= false; + exit; + end; + + numRectanglesToProcess:= a.freeRectangles.count; + + i:=0; + while i < numRectanglesToProcess do + begin + if splitFreeNode(a.freeRectangles, a.freeRectangles.data[i], newNode) then + begin + rectangleListRemoveAt(a.freeRectangles, i); + dec(numRectanglesToProcess); + end + else + inc(i); + end; + + pruneFreeList(a.freeRectangles); + newNode.UserData:= sz.UserData; + rectangleListAdd(a.usedRectangles, newNode); + output:= newNode; + atlasInsertAdaptive:= true; +end; + +procedure placeRect(var a: Atlas; node: Rectangle); +var + numRectanglesToProcess: LongInt; + i: LongInt; +begin + numRectanglesToProcess:= a.freeRectangles.Count; + + i:= 0; + while i < numRectanglesToProcess do + begin + if not splitFreeNode(a.freeRectangles, a.freeRectangles.data[i], node) then + inc(i) + else + begin + rectangleListRemoveAt(a.freeRectangles, i); + dec(numRectanglesToProcess); + end; + end; + + pruneFreeList(a.freeRectangles); + rectangleListAdd(a.usedRectangles, node); +end; + + +function atlasInsertSet(var a: Atlas; var input: SizeList; var outputs: RectangleList): boolean; +var + bestScore1, bestScore2, bestRectIndex: LongInt; + score1, score2: LongInt; + bestNode, newNode: Rectangle; + i: LongInt; + sz: Size; +begin + atlasInsertSet:= false; + + while input.count > 0 do + begin + bestScore1:= $7FFFFFFF; + bestScore2:= $7FFFFFFF; + bestRectIndex:= -1; + + for i:=0 to pred(input.count) do + begin + sz:= input.data[i]; + newNode:= scoreRect(a.freeRectangles, sz.width, sz.height, score1, score2); + + if (score1 >= bestScore1) and ((score1 <> bestScore1) or (score2 >= bestScore2)) then + continue; + + bestScore1:= score1; + bestScore2:= score2; + bestNode:= newNode; + bestRectIndex:= i; + end; + + if bestRectIndex = -1 then + exit; + + bestNode.UserData:= input.data[bestRectIndex].UserData; + placeRect(a, bestNode); + rectangleListAdd(outputs, bestNode); + sizeListRemoveAt(input, bestRectIndex); + end; + atlasInsertSet:= true; +end; + +function atlasNew(width, height: LongInt): Atlas; +var + a: Atlas; + r: Rectangle; +begin + rectangleListInit(a.freeRectangles); + rectangleListInit(a.usedRectangles); + + a.width:= width; + a.height:= height; + r.x:= 0; + r.y:= 0; + r.width:= width; + r.height:= height; + rectangleListAdd(a.freeRectangles, r); + + atlasNew:=a; +end; + +procedure atlasDelete(var a: atlas); +begin + rectangleListClear(a.freeRectangles); + rectangleListClear(a.usedRectangles); +end; + +procedure atlasReset(var a: atlas); +begin + atlasDelete(a); + a:=atlasNew(a.width, a.height); +end; + +begin +end. diff -r 390d76b29ed0 -r c633d00dc593 hedgewars/uChat.pas --- a/hedgewars/uChat.pas Mon Jun 25 09:40:56 2012 -0400 +++ b/hedgewars/uChat.pas Mon Jun 25 15:46:49 2012 +0200 @@ -31,7 +31,7 @@ procedure KeyPressChat(Key: Longword); implementation -uses SDLh, uInputHandler, uTypes, uVariables, uCommands, uUtils, uTextures, uRender, uIO; +uses SDLh, uInputHandler, uTypes, uVariables, uCommands, uUtils, uTextures, uRender, uStore, uIO; const MaxStrIndex = 27; @@ -96,9 +96,7 @@ SDL_FreeSurface(strSurface); cl.Time:= RealTicks + 12500; -cl.Tex:= Surface2Tex(resSurface, false); - -SDL_FreeSurface(resSurface) +cl.Tex:= Surface2Atlas(resSurface, false); end; // For uStore texture recreation diff -r 390d76b29ed0 -r c633d00dc593 hedgewars/uGearsRender.pas --- a/hedgewars/uGearsRender.pas Mon Jun 25 09:40:56 2012 -0400 +++ b/hedgewars/uGearsRender.pas Mon Jun 25 15:46:49 2012 +0200 @@ -37,7 +37,7 @@ end; implementation -uses uRender, uUtils, uVariables, uAmmos, Math, uVisualGears; +uses uRender, uStore, uUtils, uVariables, uAmmos, Math, uVisualGears; procedure DrawRopeLinesRQ(Gear: PGear); begin @@ -54,20 +54,18 @@ glDisable(GL_TEXTURE_2D); //glEnable(GL_LINE_SMOOTH); - glPushMatrix; - - glTranslatef(WorldDx, WorldDy, 0); + ResetRotation; + SetOffset(WorldDx, WorldDy); + UpdateModelview; glLineWidth(4.0); Tint($C0, $C0, $C0, $FF); - glVertexPointer(2, GL_FLOAT, 0, @RopePoints.rounded[0]); + SetVertexPointer(@RopePoints.rounded[0]); glDrawArrays(GL_LINE_STRIP, 0, RopePoints.Count + 2); Tint($FF, $FF, $FF, $FF); - glPopMatrix; - glEnable(GL_TEXTURE_2D); //glDisable(GL_LINE_SMOOTH) end diff -r 390d76b29ed0 -r c633d00dc593 hedgewars/uLandTexture.pas --- a/hedgewars/uLandTexture.pas Mon Jun 25 09:40:56 2012 -0400 +++ b/hedgewars/uLandTexture.pas Mon Jun 25 15:46:49 2012 +0200 @@ -99,7 +99,7 @@ with LandTextures[x, y] do begin tex:= NewTexture(TEXSIZE, TEXSIZE, Pixels(x, y)); - glBindTexture(GL_TEXTURE_2D, tex^.id); + glBindTexture(GL_TEXTURE_2D, tex^.atlas^.id); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_PRIORITY, tpHigh); end else @@ -156,7 +156,7 @@ if not isEmpty then begin if tex = nil then tex:= NewTexture(TEXSIZE, TEXSIZE, Pixels(x, y)); - glBindTexture(GL_TEXTURE_2D, tex^.id); + glBindTexture(GL_TEXTURE_2D, tex^.atlas^.id); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, TEXSIZE, TEXSIZE, 0, GL_RGBA, GL_UNSIGNED_BYTE, Pixels(x,y)); end else if tex <> nil then diff -r 390d76b29ed0 -r c633d00dc593 hedgewars/uMatrix.pas --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hedgewars/uMatrix.pas Mon Jun 25 15:46:49 2012 +0200 @@ -0,0 +1,123 @@ +(* + * Hedgewars, a free turn based strategy game + * Copyright (c) 2004-2012 Andrey Korotaev + * + * 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 390d76b29ed0 -r c633d00dc593 hedgewars/uRender.pas --- a/hedgewars/uRender.pas Mon Jun 25 09:40:56 2012 -0400 +++ b/hedgewars/uRender.pas Mon Jun 25 15:46:49 2012 +0200 @@ -22,7 +22,7 @@ interface -uses SDLh, uTypes, GLunit, uConsts; +uses SDLh, uTypes, GLunit, uConsts, uTextures, math; procedure DrawSprite (Sprite: TSprite; X, Y, Frame: LongInt); procedure DrawSprite (Sprite: TSprite; X, Y, FrameX, FrameY: LongInt); @@ -48,14 +48,78 @@ procedure DrawHedgehog (X, Y: LongInt; Dir: LongInt; Pos, Step: LongWord; Angle: real); procedure DrawScreenWidget (widget: POnScreenWidget); -procedure Tint (r, g, b, a: Byte); inline; -procedure Tint (c: Longword); inline; +// This is just temporary and becomes non public once everything changed to GL2 +procedure UpdateModelview; +procedure ResetModelview; +procedure SetOffset(X, Y: Longint); +procedure ResetRotation; implementation -uses uVariables; +uses uVariables, uStore; + +const DegToRad = 0.01745329252; // 2PI / 360 + +procedure UpdateModelview; +begin +{$IFDEF GL2} + UpdateModelviewProjection; +{$ELSE} + glLoadMatrixf(@mModelview[0,0]); +{$ENDIF} +end; + +procedure ResetModelview; +begin + mModelview[0,0]:= 1.0; mModelview[1,0]:=0.0; mModelview[3,0]:= 0; + mModelview[0,1]:= 0.0; mModelview[1,1]:=1.0; mModelview[3,1]:= 0; + UpdateModelview; +end; + +procedure SetOffset(X, Y: Longint); +begin + mModelview[3,0]:= X; + mModelview[3,1]:= Y; +end; + +procedure AddOffset(X, Y: GLfloat); // probably want to refactor this to use integers +begin + mModelview[3,0]:=mModelview[3,0] + mModelview[0,0]*X + mModelview[1,0]*Y; + mModelview[3,1]:=mModelview[3,1] + mModelview[0,1]*X + mModelview[1,1]*Y; +end; -var LastTint: LongWord = 0; +procedure SetScale(Scale: GLfloat); +begin + mModelview[0,0]:= Scale; + mModelview[1,1]:= Scale; +end; + +procedure AddScale(Scale: GLfloat); +begin + mModelview[0,0]:= mModelview[0,0]*Scale; mModelview[1,0]:= mModelview[1,0]*Scale; + mModelview[0,1]:= mModelview[0,1]*Scale; mModelview[1,1]:= mModelview[1,1]*Scale; +end; + +procedure AddScale(X, Y: GLfloat); +begin + mModelview[0,0]:= mModelview[0,0]*X; mModelview[1,0]:= mModelview[1,0]*Y; + mModelview[0,1]:= mModelview[0,1]*X; mModelview[1,1]:= mModelview[1,1]*Y; +end; + + +procedure SetRotation(Angle, ZAxis: GLfloat); +var s, c: Extended; +begin + SinCos(Angle*DegToRad, s, c); + mModelview[0,0]:= c; mModelview[1,0]:=-s*ZAxis; + mModelview[0,1]:= s*ZAxis; mModelview[1,1]:= c; +end; + +procedure ResetRotation; +begin + mModelview[0,0]:= 1.0; mModelview[1,0]:=0.0; + mModelview[0,1]:= 0.0; mModelview[1,1]:=1.0; +end; procedure DrawSpriteFromRect(Sprite: TSprite; r: TSDL_Rect; X, Y, Height, Position: LongInt); begin @@ -71,8 +135,7 @@ procedure DrawTextureFromRect(X, Y, W, H: LongInt; r: PSDL_Rect; SourceTexture: PTexture); var rr: TSDL_Rect; - _l, _r, _t, _b: real; - VertexBuffer, TextureBuffer: array [0..3] of TVertex2f; + VertexBuffer, TextureBuffer: TVertexRect; begin if (SourceTexture^.h = 0) or (SourceTexture^.w = 0) then exit; @@ -88,12 +151,9 @@ rr.w:= W; rr.h:= H; -_l:= r^.x / SourceTexture^.w * SourceTexture^.rx; -_r:= (r^.x + r^.w) / SourceTexture^.w * SourceTexture^.rx; -_t:= r^.y / SourceTexture^.h * SourceTexture^.ry; -_b:= (r^.y + r^.h) / SourceTexture^.h * SourceTexture^.ry; +glBindTexture(GL_TEXTURE_2D, SourceTexture^.atlas^.id); -glBindTexture(GL_TEXTURE_2D, SourceTexture^.id); +ComputeTexcoords(SourceTexture, r, @TextureBuffer); VertexBuffer[0].X:= X; VertexBuffer[0].Y:= Y; @@ -104,17 +164,8 @@ VertexBuffer[3].X:= X; VertexBuffer[3].Y:= rr.h + Y; -TextureBuffer[0].X:= _l; -TextureBuffer[0].Y:= _t; -TextureBuffer[1].X:= _r; -TextureBuffer[1].Y:= _t; -TextureBuffer[2].X:= _r; -TextureBuffer[2].Y:= _b; -TextureBuffer[3].X:= _l; -TextureBuffer[3].Y:= _b; - -glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]); -glTexCoordPointer(2, GL_FLOAT, 0, @TextureBuffer[0]); +SetVertexPointer(@VertexBuffer[0]); +SetTexCoordPointer(@TextureBuffer[0]); glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer)); end; @@ -125,18 +176,17 @@ procedure DrawTexture(X, Y: LongInt; Texture: PTexture; Scale: GLfloat); begin - -glPushMatrix; -glTranslatef(X, Y, 0); -glScalef(Scale, Scale, 1); +SetOffset(X, Y); +ResetRotation; +SetScale(Scale); +UpdateModelview; -glBindTexture(GL_TEXTURE_2D, Texture^.id); +glBindTexture(GL_TEXTURE_2D, Texture^.atlas^.id); -glVertexPointer(2, GL_FLOAT, 0, @Texture^.vb); -glTexCoordPointer(2, GL_FLOAT, 0, @Texture^.tb); +SetVertexPointer(@Texture^.vb); +SetTexCoordPointer(@Texture^.tb); glDrawArrays(GL_TRIANGLE_FAN, 0, Length(Texture^.vb)); - -glPopMatrix +ResetModelview; end; procedure DrawTextureF(Texture: PTexture; Scale: GLfloat; X, Y, Frame, Dir, w, h: LongInt); @@ -145,8 +195,8 @@ end; procedure DrawTextureRotatedF(Texture: PTexture; Scale, OffsetX, OffsetY: GLfloat; X, Y, Frame, Dir, w, h: LongInt; Angle: real); -var ft, fb, fl, fr: GLfloat; - hw, nx, ny: LongInt; +var hw, nx, ny: LongInt; + r: TSDL_Rect; VertexBuffer, TextureBuffer: array [0..3] of TVertex2f; begin // do not draw anything outside the visible screen space (first check fixes some sprite drawing, e.g. hedgehogs) @@ -155,14 +205,13 @@ if (abs(Y) > H) and ((abs(Y + OffsetY - (0.5 * cScreenHeight)) - W / 2) * cScaleFactor > cScreenHeight) then exit; -glPushMatrix; -glTranslatef(X, Y, 0); +SetOffset(X, Y); if Dir = 0 then Dir:= 1; -glRotatef(Angle, 0, 0, Dir); - -glTranslatef(Dir*OffsetX, OffsetY, 0); -glScalef(Scale, Scale, 1); +SetRotation(Angle, Dir); +AddOffset(Dir*OffsetX, OffsetY); +AddScale(Scale); +UpdateModelview; // Any reason for this call? And why only in t direction, not s? //glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); @@ -172,12 +221,13 @@ nx:= round(Texture^.w / w); // number of horizontal frames ny:= round(Texture^.h / h); // number of vertical frames -ft:= (Frame mod ny) * Texture^.ry / ny; -fb:= ((Frame mod ny) + 1) * Texture^.ry / ny; -fl:= (Frame div ny) * Texture^.rx / nx; -fr:= ((Frame div ny) + 1) * Texture^.rx / nx; +r.y:=(Frame mod ny) * h; +r.x:=(Frame div ny) * w; +r.w:=w; +r.h:=h; +ComputeTexcoords(Texture, @r, @TextureBuffer); -glBindTexture(GL_TEXTURE_2D, Texture^.id); +glBindTexture(GL_TEXTURE_2D, Texture^.atlas^.id); VertexBuffer[0].X:= -hw; VertexBuffer[0].Y:= w / -2; @@ -188,20 +238,11 @@ VertexBuffer[3].X:= -hw; VertexBuffer[3].Y:= w / 2; -TextureBuffer[0].X:= fl; -TextureBuffer[0].Y:= ft; -TextureBuffer[1].X:= fr; -TextureBuffer[1].Y:= ft; -TextureBuffer[2].X:= fr; -TextureBuffer[2].Y:= fb; -TextureBuffer[3].X:= fl; -TextureBuffer[3].Y:= fb; - -glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]); -glTexCoordPointer(2, GL_FLOAT, 0, @TextureBuffer[0]); +SetVertexPointer(@VertexBuffer[0]); +SetTexCoordPointer(@TextureBuffer[0]); glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer)); -glPopMatrix +ResetModelview; end; procedure DrawSpriteRotated(Sprite: TSprite; X, Y, Dir: LongInt; Angle: real); @@ -214,19 +255,18 @@ procedure DrawSpriteRotatedF(Sprite: TSprite; X, Y, Frame, Dir: LongInt; Angle: real); begin -glPushMatrix; -glTranslatef(X, Y, 0); - +SetOffset(X, Y); if Dir < 0 then - glRotatef(Angle, 0, 0, -1) + SetRotation(Angle, -1.0) else - glRotatef(Angle, 0, 0, 1); + SetRotation(Angle, 1.0); if Dir < 0 then - glScalef(-1.0, 1.0, 1.0); + AddScale(-1.0, 1.0); +UpdateModelview; DrawSprite(Sprite, -SpritesData[Sprite].Width div 2, -SpritesData[Sprite].Height div 2, Frame); -glPopMatrix +ResetModelview; end; procedure DrawTextureRotated(Texture: PTexture; hw, hh, X, Y, Dir: LongInt; Angle: real); @@ -238,19 +278,18 @@ if (abs(Y) > 2 * hh) and ((abs(Y - 0.5 * cScreenHeight) - hh) > cScreenHeight / cScaleFactor) then exit; -glPushMatrix; -glTranslatef(X, Y, 0); +SetOffset(X, Y); if Dir < 0 then begin hw:= - hw; - glRotatef(Angle, 0, 0, -1); + SetRotation(Angle, -1.0); end else - glRotatef(Angle, 0, 0, 1); + SetRotation(Angle, 1.0); +UpdateModelview; - -glBindTexture(GL_TEXTURE_2D, Texture^.id); +glBindTexture(GL_TEXTURE_2D, Texture^.atlas^.id); VertexBuffer[0].X:= -hw; VertexBuffer[0].Y:= -hh; @@ -261,11 +300,11 @@ VertexBuffer[3].X:= -hw; VertexBuffer[3].Y:= hh; -glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]); -glTexCoordPointer(2, GL_FLOAT, 0, @Texture^.tb); +SetVertexPointer(@VertexBuffer[0]); +SetTexCoordPointer(@Texture^.tb); glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer)); -glPopMatrix +ResetModelview; end; procedure DrawSprite(Sprite: TSprite; X, Y, Frame: LongInt); @@ -329,8 +368,9 @@ glDisable(GL_TEXTURE_2D); glEnable(GL_LINE_SMOOTH); - glPushMatrix; - glTranslatef(WorldDx, WorldDy, 0); + ResetRotation; + SetOffset(WorldDx, WorldDy); + UpdateModelview; glLineWidth(Width); Tint(r, g, b, a); @@ -339,11 +379,11 @@ VertexBuffer[1].X:= X1; VertexBuffer[1].Y:= Y1; - glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]); + SetVertexPointer(@VertexBuffer[0]); glDrawArrays(GL_LINES, 0, Length(VertexBuffer)); Tint($FF, $FF, $FF, $FF); - glPopMatrix; + ResetModelview; glEnable(GL_TEXTURE_2D); glDisable(GL_LINE_SMOOTH); @@ -352,6 +392,10 @@ procedure DrawFillRect(r: TSDL_Rect); var VertexBuffer: array [0..3] of TVertex2f; begin +SetOffset(0, 0); +ResetRotation; +UpdateModelview; + // do not draw anything outside the visible screen space (first check fixes some sprite drawing, e.g. hedgehogs) if (abs(r.x) > r.w) and ((abs(r.x + r.w / 2) - r.w / 2) * cScaleFactor > cScreenWidth) then exit; @@ -371,11 +415,13 @@ VertexBuffer[3].X:= r.x; VertexBuffer[3].Y:= r.y + r.h; -glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]); +SetVertexPointer(@VertexBuffer[0]); glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer)); Tint($FF, $FF, $FF, $FF); -glEnable(GL_TEXTURE_2D) +glEnable(GL_TEXTURE_2D); + +ResetModelview; end; procedure DrawCircle(X, Y, Radius, Width: LongInt; r, g, b, a: Byte); @@ -396,23 +442,29 @@ end; glDisable(GL_TEXTURE_2D); glEnable(GL_LINE_SMOOTH); - glPushMatrix; + SetOffset(0, 0); + ResetRotation; + UpdateModelview; glLineWidth(Width); - glVertexPointer(2, GL_FLOAT, 0, @CircleVertex[0]); + SetVertexPointer(@CircleVertex[0]); glDrawArrays(GL_LINE_LOOP, 0, 60); - glPopMatrix; glEnable(GL_TEXTURE_2D); glDisable(GL_LINE_SMOOTH); + ResetModelview; end; procedure DrawHedgehog(X, Y: LongInt; Dir: LongInt; Pos, Step: LongWord; Angle: real); -const VertexBuffer: array [0..3] of TVertex2f = ( - (X: -16; Y: -16), - (X: 16; Y: -16), - (X: 16; Y: 16), - (X: -16; Y: 16)); -var l, r, t, b: real; +const VertexBuffers: array[0..1] of TVertexRect = ( + ((x: -16; y: -16), + (x: 16; y: -16), + (x: 16; y: 16), + (x: -16; y: 16)), + ((x: 16; y: -16), + (x: -16; y: -16), + (x: -16; y: 16), + (x: 16; y: 16))); +var r: TSDL_Rect; TextureBuffer: array [0..3] of TVertex2f; begin // do not draw anything outside the visible screen space (first check fixes some sprite drawing, e.g. hedgehogs) @@ -421,41 +473,25 @@ if (abs(Y) > 32) and ((abs(Y - 0.5 * cScreenHeight) - 16) * cScaleFactor > cScreenHeight) then exit; - t:= Pos * 32 / HHTexture^.h; - b:= (Pos + 1) * 32 / HHTexture^.h; + r.x:=Step * 32; + r.y:=Pos * 32; + r.w:=32; + r.h:=32; + ComputeTexcoords(HHTexture, @r, @TextureBuffer); - if Dir = -1 then - begin - l:= (Step + 1) * 32 / HHTexture^.w; - r:= Step * 32 / HHTexture^.w - end - else - begin - l:= Step * 32 / HHTexture^.w; - r:= (Step + 1) * 32 / HHTexture^.w - end; - + SetOffset(X, Y); + SetRotation(Angle, 1.0); + UpdateModelview; - glPushMatrix(); - glTranslatef(X, Y, 0); - glRotatef(Angle, 0, 0, 1); - - glBindTexture(GL_TEXTURE_2D, HHTexture^.id); + glBindTexture(GL_TEXTURE_2D, HHTexture^.atlas^.id); + if Dir = -1 then + SetVertexPointer(@VertexBuffers[1][0]) + else + SetVertexPointer(@VertexBuffers[0][0]); + SetTexCoordPointer(@TextureBuffer[0]); + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - TextureBuffer[0].X:= l; - TextureBuffer[0].Y:= t; - TextureBuffer[1].X:= r; - TextureBuffer[1].Y:= t; - TextureBuffer[2].X:= r; - TextureBuffer[2].Y:= b; - TextureBuffer[3].X:= l; - TextureBuffer[3].Y:= b; - - glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]); - glTexCoordPointer(2, GL_FLOAT, 0, @TextureBuffer[0]); - glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer)); - - glPopMatrix + ResetModelview; end; procedure DrawScreenWidget(widget: POnScreenWidget); @@ -505,31 +541,4 @@ {$ENDIF} end; -procedure Tint(r, g, b, a: Byte); inline; -var nc, tw: Longword; -begin - nc:= (a shl 24) or (b shl 16) or (g shl 8) or r; - - if nc = lastTint then - exit; - - if GrayScale then - begin - tw:= round(r * RGB_LUMINANCE_RED + g * RGB_LUMINANCE_GREEN + b * RGB_LUMINANCE_BLUE); - if tw > 255 then - tw:= 255; - r:= tw; - g:= tw; - b:= tw - end; - - glColor4ub(r, g, b, a); - lastTint:= nc; -end; - -procedure Tint(c: Longword); inline; -begin - Tint(((c shr 24) and $FF), ((c shr 16) and $FF), (c shr 8) and $FF, (c and $FF)) -end; - end. diff -r 390d76b29ed0 -r c633d00dc593 hedgewars/uRenderUtils.pas --- a/hedgewars/uRenderUtils.pas Mon Jun 25 09:40:56 2012 -0400 +++ b/hedgewars/uRenderUtils.pas Mon Jun 25 15:46:49 2012 +0200 @@ -272,9 +272,7 @@ TryDo(SDL_SetColorKey(finalSurface, SDL_SRCCOLORKEY, 0) = 0, errmsgTransparentSet, true); - RenderStringTexLim:= Surface2Tex(finalSurface, false); - - SDL_FreeSurface(finalSurface); + RenderStringTexLim:= Surface2Atlas(finalSurface, false); end; @@ -459,10 +457,9 @@ inc(pos); end; - RenderSpeechBubbleTex:= Surface2Tex(finalSurface, true); + RenderSpeechBubbleTex:= Surface2Atlas(finalSurface, true); SDL_FreeSurface(rotatedEdge); - SDL_FreeSurface(finalSurface); end; end. diff -r 390d76b29ed0 -r c633d00dc593 hedgewars/uScript.pas --- a/hedgewars/uScript.pas Mon Jun 25 09:40:56 2012 -0400 +++ b/hedgewars/uScript.pas Mon Jun 25 15:46:49 2012 +0200 @@ -738,8 +738,7 @@ DrawRoundRect(@rr, clan^.Color, clan^.Color, texsurf, false); FreeTexture(team^.HealthTex); - team^.HealthTex:= Surface2Tex(texsurf, false); - SDL_FreeSurface(texsurf); + team^.HealthTex:= Surface2Atlas(texsurf, false); MakeCrossHairs end end; diff -r 390d76b29ed0 -r c633d00dc593 hedgewars/uStore.pas --- a/hedgewars/uStore.pas Mon Jun 25 09:40:56 2012 -0400 +++ b/hedgewars/uStore.pas Mon Jun 25 15:46:49 2012 +0200 @@ -43,10 +43,23 @@ procedure WarpMouse(x, y: Word); inline; procedure SwapBuffers; inline; +procedure UpdateProjection; + +{$IFDEF GL2} +procedure UpdateModelviewProjection; +{$ENDIF} + +procedure Tint(r, g, b, a: Byte); inline; +procedure Tint(c: Longword); inline; +procedure SetTexCoordPointer(p: Pointer); +procedure SetVertexPointer(p: Pointer); +procedure SetColorPointer(p: Pointer); +procedure BeginWater; +procedure EndWater; implementation uses uMisc, uConsole, uMobile, uVariables, uUtils, uTextures, uRender, uRenderUtils, uCommands, - uDebug{$IFDEF USE_CONTEXT_RESTORE}, uWorld{$ENDIF}; + uDebug{$IFDEF USE_CONTEXT_RESTORE}, uWorld{$ENDIF}, uMatrix; //type TGPUVendor = (gvUnknown, gvNVIDIA, gvATI, gvIntel, gvApple); @@ -57,6 +70,27 @@ {$ELSE} SDLPrimSurface: PSDL_Surface; {$ENDIF} +{$IFDEF GL2} + shaderMain: GLuint; + shaderWater: GLuint; + + // attributes +const + aVertex: GLint = 0; + aTexCoord: GLint = 1; + aColor: GLint = 2; + +var + uCurrentMVPLocation: GLint; + + uMainMVPLocation: GLint; + uMainTintLocation: GLint; + + uWaterMVPLocation: GLint; + +{$ENDIF} + LastTint: LongWord = 0; + function WriteInRect(Surface: PSDL_Surface; X, Y: LongInt; Color: LongWord; Font: THWFont; s: ansistring): TSDL_Rect; var w, h: LongInt; @@ -121,8 +155,7 @@ SDL_UnlockSurface(texsurf); FreeTexture(CrosshairTex); - CrosshairTex:= Surface2Tex(texsurf, false); - SDL_FreeSurface(texsurf) + CrosshairTex:= Surface2Atlas(texsurf, false); end; SDL_FreeSurface(tmpsurf) @@ -155,8 +188,7 @@ rr:= r; inc(rr.x, 2); dec(rr.w, 4); inc(rr.y, 2); dec(rr.h, 4); DrawRoundRect(@rr, Clan^.Color, Clan^.Color, texsurf, false); - HealthTex:= Surface2Tex(texsurf, false); - SDL_FreeSurface(texsurf); + HealthTex:= Surface2Atlas(texsurf, false); r.x:= 0; r.y:= 0; @@ -196,8 +228,7 @@ PLongwordArray(texsurf^.pixels)^[32 * 16 + 2]:= cNearBlackColor; PLongwordArray(texsurf^.pixels)^[32 * 16 + 23]:= cNearBlackColor; - FlagTex:= Surface2Tex(texsurf, false); - SDL_FreeSurface(texsurf); + FlagTex:= Surface2Atlas(texsurf, false); texsurf:= nil; AIKillsTex := RenderStringTex(inttostr(stats.AIKills), Clan^.Color, fnt16); @@ -229,8 +260,7 @@ r.w:= 28; r.h:= 28; DrawRoundRect(@r, cWhiteColor, cNearBlackColor, iconsurf, true); - ropeIconTex:= Surface2Tex(iconsurf, false); - SDL_FreeSurface(iconsurf); + ropeIconTex:= Surface2Atlas(iconsurf, false); iconsurf:= nil; end; end; @@ -265,8 +295,7 @@ texsurf:= LoadImage(UserPathz[ptGraves] + '/Statue', ifTransparent); if texsurf = nil then texsurf:= LoadImage(Pathz[ptGraves] + '/Statue', ifCritical or ifTransparent); - GraveTex:= Surface2Tex(texsurf, false); - SDL_FreeSurface(texsurf) + GraveTex:= Surface2Atlas(texsurf, false); end end; @@ -363,12 +392,12 @@ end; if (ii in [sprSky, sprSkyL, sprSkyR, sprHorizont, sprHorizontL, sprHorizontR]) then begin - Texture:= Surface2Tex(tmpsurf, true); + Texture:= Surface2Atlas(tmpsurf, true); Texture^.Scale:= 2 end else begin - Texture:= Surface2Tex(tmpsurf, false); + Texture:= Surface2Atlas(tmpsurf, false); // HACK: We should include some sprite attribute to define the texture wrap directions if ((ii = sprWater) or (ii = sprSDWater)) and ((cReducedQuality and (rq2DWater or rqClampLess)) = 0) then glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); @@ -382,8 +411,8 @@ {$ELSE} if saveSurf then Surface:= tmpsurf - else - SDL_FreeSurface(tmpsurf) + //else + // SDL_FreeSurface(tmpsurf) released by FreeTexture {$ENDIF} end end @@ -398,8 +427,7 @@ if tmpsurf = nil then tmpsurf:= LoadImage(Pathz[ptGraphics] + '/' + cHHFileName, ifAlpha or ifCritical or ifTransparent); -HHTexture:= Surface2Tex(tmpsurf, false); -SDL_FreeSurface(tmpsurf); +HHTexture:= Surface2Atlas(tmpsurf, false); InitHealth; @@ -419,8 +447,7 @@ TryDo(tmpsurf <> nil,'Name-texture creation for ammo type #' + intToStr(ord(ai)) + ' failed!',true); tmpsurf:= doSurfaceConversion(tmpsurf); FreeTexture(NameTex); - NameTex:= Surface2Tex(tmpsurf, false); - SDL_FreeSurface(tmpsurf) + NameTex:= Surface2Atlas(tmpsurf, false); end; // number of weapons in ammo menu @@ -429,8 +456,7 @@ tmpsurf:= TTF_RenderUTF8_Blended(Fontz[fnt16].Handle, Str2PChar(IntToStr(i) + 'x'), cWhiteColorChannels); tmpsurf:= doSurfaceConversion(tmpsurf); FreeTexture(CountTexz[i]); - CountTexz[i]:= Surface2Tex(tmpsurf, false); - SDL_FreeSurface(tmpsurf) + CountTexz[i]:= Surface2Atlas(tmpsurf, false); end; if not reload then @@ -449,7 +475,7 @@ SpritesData[ii].Texture:= nil; if (SpritesData[ii].Surface <> nil) and (not reload) then begin - SDL_FreeSurface(SpritesData[ii].Surface); + //SDL_FreeSurface(SpritesData[ii].Surface); released by FreeTexture SpritesData[ii].Surface:= nil end end; @@ -582,10 +608,7 @@ FreeTexture(HHGear^.Hedgehog^.HatTex); // assign new hat to hedgehog - HHGear^.Hedgehog^.HatTex:= Surface2Tex(texsurf, true); - - // cleanup: free temporary surface mem - SDL_FreeSurface(texsurf) + HHGear^.Hedgehog^.HatTex:= Surface2Atlas(texsurf, true); end; end; @@ -625,6 +648,112 @@ SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1); // try to prefer hardware rendering end; +{$IFDEF GL2} +function CompileShader(shaderFile: string; shaderType: GLenum): GLuint; +var + shader: GLuint; + f: Textfile; + source, line: AnsiString; + sourceA: Pchar; + lengthA: GLint; + compileResult: GLint; + logLength: GLint; + log: PChar; +begin + Assign(f, Pathz[ptShaders] + '/' + shaderFile); + filemode:= 0; // readonly + Reset(f); + if IOResult <> 0 then + begin + AddFileLog('Unable to load ' + shaderFile); + halt(-1); + end; + + source:=''; + while not eof(f) do + begin + ReadLn(f, line); + source:= source + line + #10; + end; + + CloseFile(f); + + WriteLnToConsole('Compiling shader: ' + Pathz[ptShaders] + '/' + shaderFile); + + sourceA:=PChar(source); + lengthA:=Length(source); + + shader:=glCreateShader(shaderType); + glShaderSource(shader, 1, @sourceA, @lengthA); + glCompileShader(shader); + glGetShaderiv(shader, GL_COMPILE_STATUS, @compileResult); + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, @logLength); + + if logLength > 1 then + begin + GetMem(log, logLength); + glGetShaderInfoLog(shader, logLength, nil, log); + WriteLnToConsole('========== Compiler log =========='); + WriteLnToConsole(log); + WriteLnToConsole('==================================='); + FreeMem(log, logLength); + end; + + if compileResult <> GL_TRUE then + begin + WriteLnToConsole('Shader compilation failed, halting'); + halt(-1); + end; + + CompileShader:= shader; +end; + +function CompileProgram(shaderName: string): GLuint; +var + program_: GLuint; + vs, fs: GLuint; + linkResult: GLint; + logLength: GLint; + log: PChar; +begin + program_:= glCreateProgram(); + vs:= CompileShader(shaderName + '.vs', GL_VERTEX_SHADER); + fs:= CompileShader(shaderName + '.fs', GL_FRAGMENT_SHADER); + glAttachShader(program_, vs); + glAttachShader(program_, fs); + + glBindAttribLocation(program_, aVertex, 'vertex'); + glBindAttribLocation(program_, aTexCoord, 'texcoord'); + glBindAttribLocation(program_, aColor, 'color'); + + glLinkProgram(program_); + glDeleteShader(vs); + glDeleteShader(fs); + + glGetProgramiv(program_, GL_LINK_STATUS, @linkResult); + glGetProgramiv(program_, GL_INFO_LOG_LENGTH, @logLength); + + if logLength > 1 then + begin + GetMem(log, logLength); + glGetProgramInfoLog(program_, logLength, nil, log); + WriteLnToConsole('========== Compiler log =========='); + WriteLnToConsole(log); + WriteLnToConsole('==================================='); + FreeMem(log, logLength); + end; + + if linkResult <> GL_TRUE then + begin + WriteLnToConsole('Linking program failed, halting'); + halt(-1); + end; + + CompileProgram:= program_; +end; + +{$ENDIF} + procedure SetupOpenGL; //var vendor: shortstring = ''; var buf: array[byte] of char; @@ -682,6 +811,26 @@ AddFileLog(' \----- Extensions: ' + shortstring(pchar(glGetString(GL_EXTENSIONS)))); //TODO: don't have the Extensions line trimmed but slipt it into multiple lines +{$IFDEF GL2} + Load_GL_VERSION_2_0; + + shaderWater:= CompileProgram('water'); + glUseProgram(shaderWater); + glUniform1i(glGetUniformLocation(shaderWater, 'tex0'), 0); + uWaterMVPLocation:= glGetUniformLocation(shaderWater, 'mvp'); + + shaderMain:= CompileProgram('default'); + glUseProgram(shaderMain); + glUniform1i(glGetUniformLocation(shaderMain, 'tex0'), 0); + uMainMVPLocation:= glGetUniformLocation(shaderMain, 'mvp'); + uMainTintLocation:= glGetUniformLocation(shaderMain, 'tint'); + + uCurrentMVPLocation:= uMainMVPLocation; + + Tint(255, 255, 255, 255); + UpdateModelviewProjection; +{$ENDIF} + {$IFNDEF S3D_DISABLED} if (cStereoMode = smHorizontal) or (cStereoMode = smVertical) or (cStereoMode = smAFR) then begin @@ -729,9 +878,7 @@ glMatrixMode(GL_MODELVIEW); // prepare default translation/scaling - glLoadIdentity(); - glScalef(2.0 / cScreenWidth, -2.0 / cScreenHeight, 1.0); - glTranslatef(0, -cScreenHeight / 2, 0); + SetScale(2.0); // enable alpha blending glEnable(GL_BLEND); @@ -742,27 +889,144 @@ glDisable(GL_DITHER); // enable common states by default as they save a lot glEnable(GL_TEXTURE_2D); + +{$IFDEF GL2} + glEnableVertexAttribArray(aVertex); + glEnableVertexAttribArray(aTexCoord); +{$ELSE} glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_TEXTURE_COORD_ARRAY); +{$ENDIF} +end; + + +procedure Tint(r, g, b, a: Byte); inline; +var + nc, tw: Longword; +const + scale = 1.0/255.0; +begin + nc:= (a shl 24) or (b shl 16) or (g shl 8) or r; + + if nc = lastTint then + exit; + + if GrayScale then + begin + tw:= round(r * RGB_LUMINANCE_RED + g * RGB_LUMINANCE_GREEN + b * RGB_LUMINANCE_BLUE); + if tw > 255 then + tw:= 255; + r:= tw; + g:= tw; + b:= tw + end; + + {$IFDEF GL2} + glUniform4f(uMainTintLocation, r*scale, g*scale, b*scale, a*scale); + glColor4ub(r, g, b, a); + {$ELSE} + glColor4ub(r, g, b, a); + {$ENDIF} + lastTint:= nc; +end; + +procedure Tint(c: Longword); inline; +begin + Tint(((c shr 24) and $FF), ((c shr 16) and $FF), (c shr 8) and $FF, (c and $FF)) +end; + +procedure SetTexCoordPointer(p: Pointer); +begin + {$IFDEF GL2} + glVertexAttribPointer(aTexCoord, 2, GL_FLOAT, GL_FALSE, 0, p); + {$ELSE} + glTexCoordPointer(2, GL_FLOAT, 0, p); + {$ENDIF} +end; + +procedure SetVertexPointer(p: Pointer); +begin + {$IFDEF GL2} + glVertexAttribPointer(aVertex, 2, GL_FLOAT, GL_FALSE, 0, p); + {$ELSE} + glVertexPointer(2, GL_FLOAT, 0, p); + {$ENDIF} +end; + +procedure SetColorPointer(p: Pointer); +begin + {$IFDEF GL2} + glVertexAttribPointer(aColor, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, p); + {$ELSE} + glColorPointer(4, GL_UNSIGNED_BYTE, 0, p); + {$ENDIF} +end; + +{$IFDEF GL2} +procedure UpdateModelviewProjection; +var + mvp: TMatrix4x4f; +begin + MatrixMultiply(mvp, mProjection, mModelview); + glUniformMatrix4fv(uCurrentMVPLocation, 1, GL_FALSE, @mvp[0, 0]); +end; +{$ENDIF GL2} + +procedure UpdateProjection; +var + s: GLfloat; +begin + s:=cScaleFactor; + mProjection[0,0]:= s/cScreenWidth; mProjection[0,1]:= 0.0; mProjection[0,2]:=0.0; mProjection[0,3]:= 0.0; + mProjection[1,0]:= 0.0; mProjection[1,1]:= -s/cScreenHeight; mProjection[1,2]:=0.0; mProjection[1,3]:= 0.0; + mProjection[2,0]:= 0.0; mProjection[2,1]:= 0.0; mProjection[2,2]:=1.0; mProjection[2,3]:= 0.0; + mProjection[3,0]:= cStereoDepth; mProjection[3,1]:= s/2; mProjection[3,2]:=0.0; mProjection[3,3]:= 1.0; + + {$IFDEF GL2} + UpdateModelviewProjection; + {$ELSE} + glMatrixMode(GL_PROJECTION); + glLoadMatrixf(@mProjection[0, 0]); + glMatrixMode(GL_MODELVIEW); + {$ENDIF} end; procedure SetScale(f: GLfloat); begin -// leave immediately if scale factor did not change - if f = cScaleFactor then - exit; + // This lazy update conflicts with R7103 at the moment, missing the initial SetScale(2.0) + //if cScaleFactor <> f then + //begin + cScaleFactor:=f; + UpdateProjection; + //end; +end; - if f = cDefaultZoomLevel then - glPopMatrix // "return" to default scaling - else // other scaling - begin - glPushMatrix; // save default scaling - glLoadIdentity; - glScalef(f / cScreenWidth, -f / cScreenHeight, 1.0); - glTranslatef(0, -cScreenHeight / 2, 0); - end; +procedure BeginWater; +begin + {$IFDEF GL2} + glUseProgram(shaderWater); + uCurrentMVPLocation:=uWaterMVPLocation; + UpdateModelviewProjection; + glDisableVertexAttribArray(aTexCoord); + glEnableVertexAttribArray(aColor); + {$ELSE} + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + {$ENDIF} +end; - cScaleFactor:= f; +procedure EndWater; +begin + {$IFDEF GL2} + glUseProgram(shaderMain); + uCurrentMVPLocation:=uMainMVPLocation; + UpdateModelviewProjection; + glDisableVertexAttribArray(aColor); + glEnableVertexAttribArray(aTexCoord); + {$ELSE} + glDisableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + {$ENDIF} end; //////////////////////////////////////////////////////////////////////////////// @@ -777,11 +1041,10 @@ if texsurf = nil then texsurf:= LoadImage(Pathz[ptGraphics] + '/Progress', ifCritical or ifTransparent); - ProgrTex:= Surface2Tex(texsurf, false); + ProgrTex:= Surface2Atlas(texsurf, false); squaresize:= texsurf^.w shr 1; numsquares:= texsurf^.h div squaresize; - SDL_FreeSurface(texsurf); uMobile.GameLoading(); end; @@ -926,8 +1189,7 @@ SDL_FillRect(tmpsurf, @r, $ffffffff); SDL_UpperBlit(iconsurf, iconrect, tmpsurf, @r); -RenderHelpWindow:= Surface2Tex(tmpsurf, true); -SDL_FreeSurface(tmpsurf) +RenderHelpWindow:= Surface2Atlas(tmpsurf, true); end; procedure RenderWeaponTooltip(atype: TAmmoType); @@ -1033,11 +1295,7 @@ // chFullScr is called when there is a rotation event and needs the SetScale and SetupOpenGL to set up the new resolution // this 6 gl functions are the relevant ones and are hacked together here for optimisation glMatrixMode(GL_MODELVIEW); - glPopMatrix; - glLoadIdentity(); - glScalef(2.0 / cScreenWidth, -2.0 / cScreenHeight, 1.0); - glTranslatef(0, -cScreenHeight / 2, 0); - glViewport(0, 0, cScreenWidth, cScreenHeight); + SetScale(2.0); exit; {$ELSE} SetScale(cDefaultZoomLevel); @@ -1151,6 +1409,10 @@ procedure freeModule; begin +{$IFDEF GL2} + glDeleteProgram(shaderMain); + glDeleteProgram(shaderWater); +{$ENDIF} StoreRelease(false); TTF_Quit(); {$IFDEF SDL13} diff -r 390d76b29ed0 -r c633d00dc593 hedgewars/uTextures.pas --- a/hedgewars/uTextures.pas Mon Jun 25 09:40:56 2012 -0400 +++ b/hedgewars/uTextures.pas Mon Jun 25 15:46:49 2012 +0200 @@ -24,14 +24,15 @@ function NewTexture(width, height: Longword; buf: Pointer): PTexture; procedure Surface2GrayScale(surf: PSDL_Surface); -function Surface2Tex(surf: PSDL_Surface; enableClamp: boolean): PTexture; +function Surface2Atlas(surf: PSDL_Surface; enableClamp: boolean): PTexture; procedure FreeTexture(tex: PTexture); +procedure ComputeTexcoords(texture: PTexture; r: PSDL_Rect; tb: PVertexRect); procedure initModule; procedure freeModule; implementation -uses GLunit, uUtils, uVariables, uConsts, uDebug, uConsole; +uses GLunit, uUtils, uVariables, uConsts, uDebug, uConsole, uAtlas; var TextureList: PTexture; @@ -47,10 +48,58 @@ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) end; +procedure ComputeTexcoords(texture: PTexture; r: PSDL_Rect; tb: PVertexRect); +var x0, y0, x1, y1, tmp: Real; + w, h, aw, ah: LongInt; +const texelOffset = 0.0; +begin +aw:=texture^.atlas^.w; +ah:=texture^.atlas^.h; + +if texture^.isRotated then + begin + w:=r^.h; + h:=r^.w; + end +else + begin + w:=r^.w; + h:=r^.h; + end; + +x0:= (texture^.x + r^.x + texelOffset)/aw; +x1:= (texture^.x + r^.x + w - texelOffset)/aw; +y0:= (texture^.y + r^.y + texelOffset)/ah; +y1:= (texture^.y + r^.y + h - texelOffset)/ah; + +if (texture^.isRotated) then +begin + tb^[0].X:= x0; + tb^[0].Y:= y0; + tb^[3].X:= x1; + tb^[3].Y:= y0; + tb^[2].X:= x1; + tb^[2].Y:= y1; + tb^[1].X:= x0; + tb^[1].Y:= y1 +end else +begin + tb^[0].X:= x0; + tb^[0].Y:= y0; + tb^[1].X:= x1; + tb^[1].Y:= y0; + tb^[2].X:= x1; + tb^[2].Y:= y1; + tb^[3].X:= x0; + tb^[3].Y:= y1; +end; +end; + procedure ResetVertexArrays(texture: PTexture); +var r: TSDL_Rect; begin with texture^ do - begin +begin vb[0].X:= 0; vb[0].Y:= 0; vb[1].X:= w; @@ -59,16 +108,13 @@ vb[2].Y:= h; vb[3].X:= 0; vb[3].Y:= h; +end; - tb[0].X:= 0; - tb[0].Y:= 0; - tb[1].X:= rx; - tb[1].Y:= 0; - tb[2].X:= rx; - tb[2].Y:= ry; - tb[3].X:= 0; - tb[3].Y:= ry - end; +r.x:= 0; +r.y:= 0; +r.w:= texture^.w; +r.h:= texture^.h; +ComputeTexcoords(texture, @r, @texture^.tb); end; function NewTexture(width, height: Longword; buf: Pointer): PTexture; @@ -84,16 +130,24 @@ end; TextureList:= NewTexture; -NewTexture^.w:= width; -NewTexture^.h:= height; -NewTexture^.rx:= 1.0; -NewTexture^.ry:= 1.0; + +// Atlas allocation happens here later on. For now we just allocate one exclusive atlas per sprite +new(NewTexture^.atlas); +NewTexture^.atlas^.w:=width; +NewTexture^.atlas^.h:=height; +NewTexture^.x:=0; +NewTexture^.y:=0; +NewTexture^.w:=width; +NewTexture^.h:=height; +NewTexture^.isRotated:=false; +NewTexture^.shared:=false; +NewTexture^.surface:=nil; ResetVertexArrays(NewTexture); -glGenTextures(1, @NewTexture^.id); +glGenTextures(1, @NewTexture^.atlas^.id); -glBindTexture(GL_TEXTURE_2D, NewTexture^.id); +glBindTexture(GL_TEXTURE_2D, NewTexture^.atlas^.id); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, buf); SetTextureParameters(true); @@ -121,35 +175,50 @@ end; -function Surface2Tex(surf: PSDL_Surface; enableClamp: boolean): PTexture; +function Surface2Atlas(surf: PSDL_Surface; enableClamp: boolean): PTexture; var tw, th, x, y: Longword; tmpp: pointer; fromP4, toP4: PLongWordArray; begin -new(Surface2Tex); -Surface2Tex^.PrevTexture:= nil; -Surface2Tex^.NextTexture:= nil; + if (surf^.w <= 256) and (surf^.h <= 256) then + begin + Surface2Atlas:= Surface2Tex_(surf, enableClamp); // run the atlas side by side for debugging + ResetVertexArrays(Surface2Atlas); + exit; + end; +new(Surface2Atlas); +Surface2Atlas^.PrevTexture:= nil; +Surface2Atlas^.NextTexture:= nil; if TextureList <> nil then begin - TextureList^.PrevTexture:= Surface2Tex; - Surface2Tex^.NextTexture:= TextureList + TextureList^.PrevTexture:= Surface2Atlas; + Surface2Atlas^.NextTexture:= TextureList end; -TextureList:= Surface2Tex; +TextureList:= Surface2Atlas; + +// Atlas allocation happens here later on. For now we just allocate one exclusive atlas per sprite +new(Surface2Atlas^.atlas); -Surface2Tex^.w:= surf^.w; -Surface2Tex^.h:= surf^.h; +Surface2Atlas^.w:= surf^.w; +Surface2Atlas^.h:= surf^.h; +Surface2Atlas^.x:=0; +Surface2Atlas^.y:=0; +Surface2Atlas^.isRotated:=false; +Surface2Atlas^.surface:= surf; +Surface2Atlas^.shared:= false; + if (surf^.format^.BytesPerPixel <> 4) then begin TryDo(false, 'Surface2Tex failed, expecting 32 bit surface', true); - Surface2Tex^.id:= 0; + Surface2Atlas^.atlas^.id:= 0; exit end; -glGenTextures(1, @Surface2Tex^.id); +glGenTextures(1, @Surface2Atlas^.atlas^.id); -glBindTexture(GL_TEXTURE_2D, Surface2Tex^.id); +glBindTexture(GL_TEXTURE_2D, Surface2Atlas^.atlas^.id); if SDL_MustLock(surf) then SDLTry(SDL_LockSurface(surf) >= 0, true); @@ -164,8 +233,8 @@ tw:= toPowerOf2(Surf^.w); th:= toPowerOf2(Surf^.h); - Surface2Tex^.rx:= Surf^.w / tw; - Surface2Tex^.ry:= Surf^.h / th; + Surface2Atlas^.atlas^.w:=tw; + Surface2Atlas^.atlas^.h:=th; tmpp:= GetMem(tw * th * surf^.format^.BytesPerPixel); @@ -195,12 +264,12 @@ end else begin - Surface2Tex^.rx:= 1.0; - Surface2Tex^.ry:= 1.0; + Surface2Atlas^.atlas^.w:=Surf^.w; + Surface2Atlas^.atlas^.h:=Surf^.h; glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, surf^.w, surf^.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, surf^.pixels); end; -ResetVertexArrays(Surface2Tex); +ResetVertexArrays(Surface2Atlas); if SDL_MustLock(surf) then SDL_UnlockSurface(surf); @@ -214,19 +283,33 @@ begin if tex <> nil then begin + if tex^.shared then + begin + FreeTexture_(tex); // run atlas side by side for debugging + SDL_FreeSurface(tex^.surface); + exit; + end; + + // Atlas cleanup happens here later on. For now we just free as each sprite has one atlas + Dispose(tex^.atlas); + if tex^.NextTexture <> nil then tex^.NextTexture^.PrevTexture:= tex^.PrevTexture; if tex^.PrevTexture <> nil then tex^.PrevTexture^.NextTexture:= tex^.NextTexture else TextureList:= tex^.NextTexture; - glDeleteTextures(1, @tex^.id); + glDeleteTextures(1, @tex^.atlas^.id); + + if (tex^.surface <> nil) then + SDL_FreeSurface(tex^.surface); Dispose(tex); end end; procedure initModule; begin +uAtlas.initModule; TextureList:= nil; end; @@ -236,7 +319,7 @@ WriteToConsole('FIXME FIXME FIXME. App shutdown without full cleanup of texture list; read game0.log and please report this problem'); while TextureList <> nil do begin - AddFileLog('Texture not freed: width='+inttostr(LongInt(TextureList^.w))+' height='+inttostr(LongInt(TextureList^.h))+' priority='+inttostr(round(TextureList^.priority*1000))); + AddFileLog('Sprite not freed: width='+inttostr(LongInt(TextureList^.w))+' height='+inttostr(LongInt(TextureList^.h))+' priority='+inttostr(round(TextureList^.atlas^.priority*1000))); FreeTexture(TextureList); end end; diff -r 390d76b29ed0 -r c633d00dc593 hedgewars/uTypes.pas --- a/hedgewars/uTypes.pas Mon Jun 25 09:40:56 2012 -0400 +++ b/hedgewars/uTypes.pas Mon Jun 25 15:46:49 2012 +0200 @@ -44,7 +44,8 @@ // Different files are stored in different folders, this enumeration is used to tell which folder to use TPathType = (ptNone, ptData, ptGraphics, ptThemes, ptCurrTheme, ptTeams, ptMaps, ptMapCurrent, ptDemos, ptSounds, ptGraves, ptFonts, ptForts, - ptLocale, ptAmmoMenu, ptHedgehog, ptVoices, ptHats, ptFlags, ptMissionMaps, ptSuddenDeath, ptButtons); + ptLocale, ptAmmoMenu, ptHedgehog, ptVoices, ptHats, ptFlags, ptMissionMaps, ptSuddenDeath, ptButtons, + ptShaders); // Available sprites for displaying stuff TSprite = (sprWater, sprCloud, sprBomb, sprBigDigit, sprFrame, @@ -199,13 +200,34 @@ X, Y: GLint; end; + TMatrix4x4f = array[0..3, 0..3] of GLfloat; + + PAtlas = ^TAtlas; PTexture = ^TTexture; + + TAtlas = record + id: GLuint; + w, h: LongInt; + priority: GLfloat; + end; + + PVertexRect = ^TVertexRect; + TVertexRect = array[0..3] of TVertex2f; + TTexture = record - id: GLuint; + atlas: PAtlas; w, h, scale: LongInt; - rx, ry: GLfloat; - priority: GLfloat; - vb, tb: array [0..3] of TVertex2f; + + x, y: LongInt; // Offset in the texture atlas + isRotated: boolean; // if true sprite is flipped in the atlas taking w pixels along the y and h pixels along the x axis + + shared: boolean; // true if in an atlas, false if atlas points to a dedicated texture for this sprite + + surface: PSDL_Surface; // retained in memory surface + + // Cached values for texel coordinates and vertex coordinates + vb, tb: TVertexRect; + PrevTexture, NextTexture: PTexture; end; diff -r 390d76b29ed0 -r c633d00dc593 hedgewars/uVariables.pas --- a/hedgewars/uVariables.pas Mon Jun 25 09:40:56 2012 -0400 +++ b/hedgewars/uVariables.pas Mon Jun 25 15:46:49 2012 +0200 @@ -21,7 +21,7 @@ unit uVariables; interface -uses SDLh, uTypes, uFloat, GLunit, uConsts, Math, uMobile; +uses SDLh, uTypes, uFloat, GLunit, uConsts, Math, uMobile, uMatrix; var /////// init flags /////// @@ -225,8 +225,9 @@ 'Graphics/Hats', // ptHats 'Graphics/Flags', // ptFlags 'Missions/Maps', // ptMissionMaps - 'Graphics/SuddenDeath', // ptSuddenDeath - 'Graphics/Buttons' // ptButton + 'Graphics/SuddenDeath', // ptSuddenDeath + 'Graphics/Buttons', // ptButton + 'Shaders' // ptShaders ); Fontz: array[THWFont] of THHFont = ( @@ -2427,6 +2428,7 @@ SyncTexture, ConfirmTexture: PTexture; cScaleFactor: GLfloat; + cStereoDepth: GLfloat; SupportNPOTT: Boolean; Step: LongInt; squaresize : LongInt; @@ -2465,6 +2467,9 @@ lastTurnChecksum : Longword; + mModelview: TMatrix4x4f; + mProjection: TMatrix4x4f; + var trammo: array[TAmmoStrId] of ansistring; // name of the weapon trammoc: array[TAmmoStrId] of ansistring; // caption of the weapon trammod: array[TAmmoStrId] of ansistring; // description of the weapon @@ -2614,6 +2619,9 @@ GrayScale:= false; LuaGoals:= ''; + + MatrixLoadIdentity(mModelview); + MatrixLoadIdentity(mProjection); end; procedure freeModule; diff -r 390d76b29ed0 -r c633d00dc593 hedgewars/uWorld.pas --- a/hedgewars/uWorld.pas Mon Jun 25 09:40:56 2012 -0400 +++ b/hedgewars/uWorld.pas Mon Jun 25 15:46:49 2012 +0200 @@ -60,7 +60,8 @@ uCaptions, uCursor, uCommands, - uMobile + uMobile, + uAtlas ; var cWaveWidth, cWaveHeight: LongInt; @@ -77,7 +78,6 @@ amSel: TAmmoType = amNothing; missionTex: PTexture; missionTimer: LongInt; - stereoDepth: GLfloat; isFirstFrame: boolean; AMAnimType: LongInt; @@ -495,8 +495,7 @@ DrawLine2Surf(amSurface, AMRect.w+BORDERSIZE+i, BORDERSIZE, AMRect.w + BORDERSIZE+i, AMRect.h + BORDERSIZE, 160,160,160);//right end; -GetAmmoMenuTexture:= Surface2Tex(amSurface, false); -if amSurface <> nil then SDL_FreeSurface(amSurface); +GetAmmoMenuTexture:= Surface2Atlas(amSurface, false); end; procedure ShowAmmoMenu; @@ -795,28 +794,29 @@ VertexBuffer[3].X:= -lw; VertexBuffer[3].Y:= lh; - glDisableClientState(GL_TEXTURE_COORD_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); + BeginWater; if SuddenDeathDmg then - glColorPointer(4, GL_UNSIGNED_BYTE, 0, @SDWaterColorArray[0]) + SetColorPointer(@SDWaterColorArray[0]) else - glColorPointer(4, GL_UNSIGNED_BYTE, 0, @WaterColorArray[0]); + SetColorPointer(@WaterColorArray[0]); - glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]); + SetVertexPointer(@VertexBuffer[0]); glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer)); - glDisableClientState(GL_COLOR_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); + EndWater; + {$IFNDEF GL2} glColor4ub($FF, $FF, $FF, $FF); // must not be Tint() as color array seems to stay active and color reset is required + {$ENDIF} glEnable(GL_TEXTURE_2D); end; end; procedure DrawWaves(Dir, dX, dY: LongInt; tnt: Byte); var VertexBuffer, TextureBuffer: array [0..3] of TVertex2f; - lw, waves, shift: GLfloat; + lw, waves: GLfloat; sprite: TSprite; + r: TSDL_Rect; begin if SuddenDeathDmg then sprite:= sprSDWater @@ -826,7 +826,6 @@ cWaveWidth:= SpritesData[sprite].Width; lw:= cScreenWidth / cScaleFactor; -waves:= lw * 2 / cWaveWidth; if SuddenDeathDmg then Tint(LongInt(tnt) * SDWaterColorArray[2].r div 255 + 255 - tnt, @@ -841,7 +840,7 @@ 255 ); -glBindTexture(GL_TEXTURE_2D, SpritesData[sprite].Texture^.id); +glBindTexture(GL_TEXTURE_2D, SpritesData[sprite].Texture^.atlas^.id); VertexBuffer[0].X:= -lw; VertexBuffer[0].Y:= cWaterLine + WorldDy + dY; @@ -852,19 +851,16 @@ VertexBuffer[3].X:= -lw; VertexBuffer[3].Y:= VertexBuffer[2].Y; -shift:= - lw / cWaveWidth; -TextureBuffer[0].X:= shift + (( - WorldDx + LongInt(RealTicks shr 6) * Dir + dX) mod cWaveWidth) / (cWaveWidth - 1); -TextureBuffer[0].Y:= 0; -TextureBuffer[1].X:= TextureBuffer[0].X + waves; -TextureBuffer[1].Y:= TextureBuffer[0].Y; -TextureBuffer[2].X:= TextureBuffer[1].X; -TextureBuffer[2].Y:= SpritesData[sprite].Texture^.ry; -TextureBuffer[3].X:= TextureBuffer[0].X; -TextureBuffer[3].Y:= TextureBuffer[2].Y; +// this uses texture repeat mode, when using an atlas rect we need to split to several quads here! +r.x := -Trunc(lw) + (( - WorldDx + LongInt(RealTicks shr 6) * Dir + dX) mod cWaveWidth); +r.y:= 0; +r.w:= Trunc(lw + lw); +r.h:= SpritesData[sprite].Texture^.h; +ComputeTexcoords(SpritesData[sprite].Texture, @r, @TextureBuffer); -glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]); -glTexCoordPointer(2, GL_FLOAT, 0, @TextureBuffer[0]); +SetVertexPointer(@VertexBuffer[0]); +SetTexCoordPointer(@TextureBuffer[0]); glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer)); Tint($FF, $FF, $FF, $FF); @@ -961,7 +957,7 @@ begin glClear(GL_COLOR_BUFFER_BIT); DrawWorldStereo(Lag, rmDefault) - end + end; {$IFNDEF S3D_DISABLED} else if (cStereoMode = smAFR) then begin @@ -1069,41 +1065,24 @@ else glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE); DrawWorldStereo(Lag, rmRightEye); - end + end; {$ENDIF} + DebugAtlas; end; procedure ChangeDepth(rm: TRenderMode; d: GLfloat); begin -{$IFDEF S3D_DISABLED} - rm:= rm; d:= d; // avoid hint - exit; -{$ELSE} d:= d / 5; - if rm = rmDefault then - exit - else if rm = rmLeftEye then + if rm = rmLeftEye then d:= -d; - stereoDepth:= stereoDepth + d; - glMatrixMode(GL_PROJECTION); - glTranslatef(d, 0, 0); - glMatrixMode(GL_MODELVIEW); -{$ENDIF} + cStereoDepth:= cStereoDepth + d; + UpdateProjection; end; procedure ResetDepth(rm: TRenderMode); begin -{$IFDEF S3D_DISABLED} - rm:= rm; // avoid hint - exit; -{$ELSE} - if rm = rmDefault then - exit; - glMatrixMode(GL_PROJECTION); - glTranslatef(-stereoDepth, 0, 0); - glMatrixMode(GL_MODELVIEW); - stereoDepth:= 0; -{$ENDIF} + cStereoDepth:= 0; + UpdateProjection; end; procedure DrawWorldStereo(Lag: LongInt; RM: TRenderMode); @@ -1483,8 +1462,7 @@ tmpSurface:= TTF_RenderUTF8_Blended(Fontz[fnt16].Handle, Str2PChar(s), cWhiteColorChannels); tmpSurface:= doSurfaceConversion(tmpSurface); FreeTexture(timeTexture); - timeTexture:= Surface2Tex(tmpSurface, false); - SDL_FreeSurface(tmpSurface) + timeTexture:= Surface2Atlas(tmpSurface, false); end; if timeTexture <> nil then @@ -1501,8 +1479,7 @@ tmpSurface:= TTF_RenderUTF8_Blended(Fontz[fnt16].Handle, Str2PChar(s), cWhiteColorChannels); tmpSurface:= doSurfaceConversion(tmpSurface); FreeTexture(fpsTexture); - fpsTexture:= Surface2Tex(tmpSurface, false); - SDL_FreeSurface(tmpSurface) + fpsTexture:= Surface2Atlas(tmpSurface, false); end; if fpsTexture <> nil then DrawTexture((cScreenWidth shr 1) - 60 - offsetY, offsetX, fpsTexture); @@ -1556,7 +1533,7 @@ glDisable(GL_TEXTURE_2D); - glVertexPointer(2, GL_FLOAT, 0, @VertexBuffer[0]); + SetVertexPointer(@VertexBuffer[0]); glDrawArrays(GL_TRIANGLE_FAN, 0, Length(VertexBuffer)); glEnable(GL_TEXTURE_2D); @@ -1823,14 +1800,13 @@ missionTimer:= 0; missionTex:= nil; cOffsetY:= 0; - stereoDepth:= 0; + cStereoDepth:= 0; AMState:= AMHidden; isFirstFrame:= true; end; procedure freeModule; begin - stereoDepth:= stereoDepth; // avoid hint FreeTexture(fpsTexture); fpsTexture:= nil; FreeTexture(timeTexture); diff -r 390d76b29ed0 -r c633d00dc593 share/hedgewars/Data/CMakeLists.txt --- a/share/hedgewars/Data/CMakeLists.txt Mon Jun 25 09:40:56 2012 -0400 +++ b/share/hedgewars/Data/CMakeLists.txt Mon Jun 25 15:46:49 2012 +0200 @@ -1,3 +1,3 @@ -foreach(dir "Fonts" "Forts" "Graphics" "Locale" "Maps" "Music" "Sounds" "Themes" "Missions" "Names" "misc" "Scripts") +foreach(dir "Fonts" "Forts" "Graphics" "Locale" "Maps" "Music" "Sounds" "Themes" "Missions" "Names" "misc" "Scripts" "Shaders") add_subdirectory(${dir}) endforeach(dir) diff -r 390d76b29ed0 -r c633d00dc593 share/hedgewars/Data/Shaders/CMakeLists.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/share/hedgewars/Data/Shaders/CMakeLists.txt Mon Jun 25 15:46:49 2012 +0200 @@ -0,0 +1,5 @@ +file(GLOB BaseShaders *.vs *.fs) + +install(FILES + ${BaseShaders} + DESTINATION ${SHAREPATH}Data/Shaders) diff -r 390d76b29ed0 -r c633d00dc593 share/hedgewars/Data/Shaders/default.fs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/share/hedgewars/Data/Shaders/default.fs Mon Jun 25 15:46:49 2012 +0200 @@ -0,0 +1,13 @@ +#version 130 + +uniform sampler2D tex0; +uniform vec4 tint; + +in vec2 tex; + +out vec4 color; + +void main() +{ + color = texture(tex0, tex) * tint; +} diff -r 390d76b29ed0 -r c633d00dc593 share/hedgewars/Data/Shaders/default.vs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/share/hedgewars/Data/Shaders/default.vs Mon Jun 25 15:46:49 2012 +0200 @@ -0,0 +1,16 @@ +#version 130 + +in vec2 vertex; +in vec2 texcoord; +in vec4 colors; + +out vec2 tex; + +uniform mat4 mvp; + +void main() +{ + vec4 p = mvp * vec4(vertex, 0.0f, 1.0f); + gl_Position = p; + tex = texcoord; +} diff -r 390d76b29ed0 -r c633d00dc593 share/hedgewars/Data/Shaders/water.fs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/share/hedgewars/Data/Shaders/water.fs Mon Jun 25 15:46:49 2012 +0200 @@ -0,0 +1,9 @@ +#version 130 + +in vec4 vcolor; +out vec4 color; + +void main() +{ + color = vcolor; +} diff -r 390d76b29ed0 -r c633d00dc593 share/hedgewars/Data/Shaders/water.vs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/share/hedgewars/Data/Shaders/water.vs Mon Jun 25 15:46:49 2012 +0200 @@ -0,0 +1,14 @@ +#version 130 + +in vec2 vertex; +in vec4 color; +out vec4 vcolor; + +uniform mat4 mvp; + +void main() +{ + vec4 p = mvp * vec4(vertex, 0.0f, 1.0f); + gl_Position = p; + vcolor = color; +}