StranikS_Scan Posted December 28, 2019 Share Posted December 28, 2019 (edited) Значится продолжаю работать над реанимацией MCTCreator. Сейчас остановился на покраске танков, статически расположенных на картах. Расковырял space.bin, покопался в механизме, Значится пришел к следующему - больше патчить space.bin не требуется и я этого делать не буду. Достаточно перекрасить отдельные DDS файлы и всё. Но вот дальше есть проблемы. Файлы DDS в формате DXT1 с MipMap. Обнаружил, что они и после перекраски должны быть в сжатом формате DXT1 и обязательно иметь MipMap. Иначе клиент их не отображает. Однако на Delphi проблематично найти или написать код на сжатие в DXT1. Я нашел библиотеку GLScene под Delphi, в ней есть класс TGLDDSImage который умеет читать сжатый DDS, в этом классе я могу получить прямой доступ к каждому блоку сжатого изображения, я могу создавать MipMap, я могу сконвертировать DXT1 в Bitmap32 и его перекрасить, но увы я не могу самого главного - я не могу сконвертировать Bitmap32 обратно в DXT1. В библиотеке GLScene такая возможность просто отсутствует, такого кода нет. Точно также как я нигде не нашел его под Delphi на профильных сайтах и форумах. Потому нужна помощь, кто видел или знает как на Делфи сжать в DXT1 изображение отзовитесь. Либо как альтернативна, поделитесь компактной консольной программкой для конвертирования BMP или несжатых DDS в DXT1, я её тогда положу в комплект с MCTCreator-ом и он будет её вызывать для своих нужд. Под спойлером немного кода из GLScene с комментариями. procedure TForm1.Button1Click(Sender: TObject); var s:TGLDDSImage; i,n,size: Cardinal; newData: PGLPixel32Array; //МАССИВ ПИКСЕЛЕЙ, ГДЕ ЭЛЕМЕНТАМИ ЯВЛЯЮТСЯ ЗАПИСИ ВИДА record r,g,b,a: Byte end begin s:=TGLDDSImage.Create; //КЛАСС ДЛЯ РАБОТЫ ЧТЕНИЯ DDS из GLScene s.LoadFromFile('123.dds'); s.UnMipmap; //МОГУ УДАЛИТЬ МИПМАПЫ, МОГУ ИХ СОЗДАТЬ size := s.LevelWidth[0] * s.LevelHeight[0] * 4; GetMem(newData, size); ConvertImage( //КОНВЕРТИРУЮ МАССИВ СО СЖАТЫМ ИЗОБРАЖЕНИЕМ В МАССИВ С ПИКСЕЛЯМИ Bitmap32 s.Data, newData, s.ColorFormat, GL_RGBA, s.DataType, GL_BYTE, s.LevelWidth[0], s.LevelHeight[0]); n:=s.LevelWidth[0]*s.LevelHeight[0]; for i:=0 to n-1 do begin newData[i].r:=$FF; //ПЕРЕКРАШИВАЮ ПИКСЕЛИ newData[i].g:=$00; newData[i].b:=$00; newData[i].a:=$00; end; Caption:=IntToStr(i); ConvertImage( //------- ЭТОТ КОД НЕ РАБОТАЕТ, ТАК КАК НЕ ВОЗМОЖНО СКОНВЕРТИРОВАТЬ ОБРАТНО В DXT1, ТАКОЙ КОД ТУПО ОТСУСТВУЕТ В БИБЛИОТЕКЕ ------------- newData, s.Data, GL_RGBA, s.ColorFormat, GL_BYTE, s.DataType, s.LevelWidth[0], s.LevelHeight[0]); FreeMem(newData); s.SaveToFile('345.dds'); s.Free; end; //А ВОТ ЭТО КОД ИЗ БИБЛИОТЕКИ ДЛЯ КОНВЕРТИРОВАНИЯ МАССИА ПИКСЕЛЕЙ ИЗ DXT1 В Bitmap32 procedure DXT1_ToImf(ASource: Pointer; ADest: PIntermediateFormatArray; AColorFormat: TGLEnum; AWidth, AHeight: Integer); var x, y, i, j, k, select, offset: Integer; col0, col1: Word; colors: TU48BitBlock; bitmask: Cardinal; temp: PGLubyte; r0, g0, b0, r1, g1, b1: Byte; begin temp := PGLubyte(ASource); for y := 0 to (AHeight div 4) - 1 do begin for x := 0 to (AWidth div 4) - 1 do begin col0 := PWord(temp)^; Inc(temp, 2); col1 := PWord(temp)^; Inc(temp, 2); bitmask := PCardinal(temp)^; Inc(temp, 4); DecodeColor565(col0, r0, g0, b0); DecodeColor565(col1, r1, g1, b1); colors[0][0] := r0 shl 3; colors[0][1] := g0 shl 2; colors[0][2] := b0 shl 3; colors[0][3] := $FF; colors[1][0] := r1 shl 3; colors[1][1] := g1 shl 2; colors[1][2] := b1 shl 3; colors[1][3] := $FF; if col0 > col1 then begin colors[2][0] := (2 * colors[0][0] + colors[1][0] + 1) div 3; colors[2][1] := (2 * colors[0][1] + colors[1][1] + 1) div 3; colors[2][2] := (2 * colors[0][2] + colors[1][2] + 1) div 3; colors[2][3] := $FF; colors[3][0] := (colors[0][0] + 2 * colors[1][0] + 1) div 3; colors[3][1] := (colors[0][1] + 2 * colors[1][1] + 1) div 3; colors[3][2] := (colors[0][2] + 2 * colors[1][2] + 1) div 3; colors[3][3] := $FF; end else begin colors[2][0] := (colors[0][0] + colors[1][0]) div 2; colors[2][1] := (colors[0][1] + colors[1][1]) div 2; colors[2][2] := (colors[0][2] + colors[1][2]) div 2; colors[2][3] := $FF; colors[3][0] := (colors[0][0] + 2 * colors[1][0] + 1) div 3; colors[3][1] := (colors[0][1] + 2 * colors[1][1] + 1) div 3; colors[3][2] := (colors[0][2] + 2 * colors[1][2] + 1) div 3; colors[3][3] := 0; end; k := 0; for j := 0 to 3 do begin for i := 0 to 3 do begin select := (bitmask and (3 shl (k * 2))) shr (k * 2); if ((4 * x + i) < AWidth) and ((4 * y + j) < AHeight) then begin offset := ((4 * y + j) * AWidth + (4 * x + i)); ADest[offset].B := colors[select][0]; ADest[offset].G := colors[select][1]; ADest[offset].R := colors[select][2]; ADest[offset].A := colors[select][3]; end; Inc(k); end; end; end; end; end; ЗЫ: Меня немного удивляет почему в GLScene есть функция на декодирования DXT1 в Bitmap32 и нигде нет обратной. Хотя я не шибко разбираюсь в моделях и форматах к ним. Edited December 28, 2019 by StranikS_Scan Link to comment Short link Share on other sites More sharing options...
ktulho Posted December 28, 2019 Share Posted December 28, 2019 Я использовал Crystaldxt в CCAtlas. 18 минут назад, StranikS_Scan сказал: Меня немного удивляет почему в GLScene есть функция на декодирования DXT1 в Bitmap32 и нигде нет обратной. Наверное потому что, для декодирования существует только один алгоритм, и он описан в стандарте. А для кодирования каждый придумывает свой. 1 @ Link to comment Short link Share on other sites More sharing options...
StranikS_Scan Posted December 28, 2019 Author Share Posted December 28, 2019 15 минут назад, ktulho сказал: Я использовал Crystaldxt в CCAtlas. Наверное потому что, для декодирования существует только один алгоритм, и он описан в стандарте. А для кодирования каждый придумывает свой. Во! Попробую заюзать. Ну да, это же сжатие с потерями. Link to comment Short link Share on other sites More sharing options...
StranikS_Scan Posted December 29, 2019 Author Share Posted December 29, 2019 @ktulho а создания мипмапов в нём нет? Я чета не вижу. Link to comment Short link Share on other sites More sharing options...
yepev Posted December 29, 2019 Share Posted December 29, 2019 Во чё нагуглил: DXT Libraries stb_dxt.h is a single file, public domain DXT1/DXT5 compressor by Sean Barrett based on work by Fabian Giesen. squish is one of the oldest libraries, it dates back to 2006 and was written by Simon Brown. He links to libsquish version 1.10 which hasn't been updated in three years, but apparently others have continued development and I've seen versions as high as 1.15. Crunch takes a novel approach to DXT compression, it compresses the DXT data with the goal of making it more compressible, and then runs a compressor on that data and stores it in their own .CRN file format. There are a ton of games that use Crunch, and it is a good way to go if you are looking for maximum compression. In the end my goal was not maximum compression, and based on Crunch's documentation I thought that the Crunch utility itself did not come with full source. I found out later that the full source is on github. Basis is the next generation of Crunch and is under active development. It is a paid product and you can learn more about it here. Unity Crunch Fork is a fork of Crunch put out by the folks at Unity which makes Crunch a little faster, compress a little better and support some new bitmap formats. Learn more about it on the Unity blog. bimg is an image library which includes the texturec tool for compressing bitmaps to DXT among other formats. I found out about this too late, but you can check it out on github (texturec.) Compressonator is from AMD and is open source. I did a lot of research trying to figure out which DXT compressor produced the bast looking results. There aren't a lot of subjective comparisons, however the anecdotal consensus is that Compressonator produces the best looking DXT files. Compressonator comes in both a GUI version with an image comparison tool as well as a command line tool suitable for scripting, and it can generate MIP maps. The only glitch I ran into is that on my Windows 7 machine Compressonator tries to load a Windows 10 .DLL and pops up a error dialog. The people working on Compressonator were very quick to come up with a workaround. NVidia Texture Tools are based on libsquish but also include a GPU accelerated version. More info here. Походу Compressonator от AMD поддерживает и CLI и mipmaps, так что начать стоит с него) 1 @ Link to comment Short link Share on other sites More sharing options...
ktulho Posted December 29, 2019 Share Posted December 29, 2019 1 час назад, StranikS_Scan сказал: @ktulho а создания мипмапов в нём нет? Я чета не вижу. Вроде есть. До дома доберусь, посмотрю. Помниться мне писали, что CCAtlas с мтнимапами создавал атласы. Link to comment Short link Share on other sites More sharing options...
ktulho Posted December 29, 2019 Share Posted December 29, 2019 Действительно нет. Про мипмапы в теме TankIconMaker писали. Link to comment Short link Share on other sites More sharing options...
StranikS_Scan Posted December 29, 2019 Author Share Posted December 29, 2019 1 час назад, ktulho сказал: Действительно нет. Про мипмапы в теме TankIconMaker писали. Придётся колхозить, попробую в CrystalDXT прикрутить код из GLScene благо мипмапы не шибко сложная вещь. Link to comment Short link Share on other sites More sharing options...
ktulho Posted December 29, 2019 Share Posted December 29, 2019 @StranikS_Scan тут добавили поддержку mipmap. 1 @ Link to comment Short link Share on other sites More sharing options...
StranikS_Scan Posted December 29, 2019 Author Share Posted December 29, 2019 (edited) 21 час назад, ktulho сказал: @StranikS_Scan тут добавили поддержку mipmap. Ооо, яа, яа, зер гуд! Тема закрыта. Ниже выкладываю доработанную мной немного версию CrystalDXT, взятую с репо CrystalAtf, и пример процедуры для записи одноцветного DDS DXT1/DXT5 с альфой и мипмапом. CrystalDXT.pas //Создание DDS формата DXT1 или DXT5 с заполнением единым цветом RGBA c MipMap procedure CreateDDS(const FileName: string; const Width, Height: Cardinal; const Color: string; const Dxt5Format: Boolean; const MipMap: Boolean); var PixColor: TRGBQuad; Header: TDxtHeader; DxtFormat: AtfFormat; Buffer: TMemoryStream; DxtBlocks: Pointer; i, NumMips: Integer; Size: Cardinal; Sizes: TSizes; Bitmap: TBitmap; SLine: PRGBQuad; x, y, W, H: Cardinal; begin PixColor:=GetColor(Color); if Dxt5Format then DxtFormat:=DXT5 else DxtFormat:=DXT1; W:=Width; H:=Height; Buffer:=TMemoryStream.Create; try //Покраска Bitmap:=TBitmap.Create; try with Bitmap do begin PixelFormat:=pf32bit; Width:=W; Height:=H; for y:=0 to H-1 do begin SLine:=ScanLine[y]; for x:=0 to W-1 do begin SLine^:=PixColor; Inc(SLine); end; end; end; //Мипмапы NumMips:=0; if MipMap then begin Size:=0; while (W>1)or(H>1) do begin Sizes[NumMips]:=DxtImageSize(DxtFormat, W, H); Inc(Size, Sizes[NumMips]); if W>1 then W:=W div 2; if H>1 then H:=H div 2; Inc(NumMips); end; Sizes[NumMips]:=DxtImageSize(DxtFormat, 1, 1); Inc(Size, Sizes[NumMips]); end else begin Size:=DxtImageSize(DxtFormat, W, H); Sizes[0]:=Size; end; //Запись Header:=DxtHeader(Width, Height, NumMips+1, DxtFormat); Buffer.Write(Header, SizeOf(Header)); Buffer.Size:=SizeOf(Header)+Size; Size:=Integer(Buffer.Memory)+SizeOf(Header); DxtBlocks:=Pointer(Size); i:=0; repeat DxtImageCompressParallel(DxtBlocks, not Dxt5Format, Bitmap.Width, Bitmap.Height, Pointer(Bitmap), GetBitmap32bitBlock); if NumMips>0 then begin Inc(Size, Sizes[i]); DxtBlocks:=Pointer(Size); MipMapCompress(Bitmap); end; Inc(i); until i>NumMips; Buffer.SaveToFile(FileName); finally Bitmap.Free; end; finally Buffer.Free; end; end; Edited December 30, 2019 by StranikS_Scan Link to comment Short link Share on other sites More sharing options...
Recommended Posts