Короче, удивляюсь, почему никто раньше не заметил.
Скины монстров и вообще многие текстуры в Квейке 2 были созданы ещё *до* появления 3d ускорителей. И, соответственно, размеры у них в подавляющем большинстве - *не* кратны степени двойки. А Квака 2 создавалась во времена, когда ни один ускоритель не поддерживал текстуры больше 256*256. И угадайте, что квейк делает когда грузит скин, например, размером 309х160 ?.. Прааально, он его безбожно уродует, упрессовывая в 256х128.
Ну, исходники уже тыщу лет доступны под GNU GPL, лезем в них и снимаем идиотское ограничение, попутно привинчивая фильтр lanczos для резайсинга:
http://www.chebmaster.com/q2facelift/index_ru.html
Глюк с лайтмапами благополучно исправлен, см. мой последний пост.
Новый вариант - пор той жессылке :)
Какой стек нафиг???
Как ты сделал ресэмплинг? Наверное, у тебя с выделением памяти проблема. В Ку2 есть выделитель памяти -- Z_Malloc/Z_Free, есть еще и Hunk. Вот ими и пользуйся.
Кстати, раз уж выкладываешь бинарники, будь добр выложить и код.
Скоко видел исходников кваки, но ни один запустить не смог. Не было ресурсов токо код.
Память выделяется в виде глобальной переменной под максимальный размер текстуры (была локальная переменная под максимальный размер текстуры).
Ничего не выделяю. Может, что-то выделяет пристёгнутая мной библиотека VampyreImaging - но и то вряд ли, она ресэмплит из уже выделенного (где-то раньше, в процедуре загрузки картинки) куска прямо в эту глобальную переменную. У Дельфи собственный диспетчер памяти, и dll берёт память напрямую у WinApi.
Падать начало когда увеличил размер этого массива (тогда ещё - локальной переменной). Вывод?.. Слишком малый лимит стека. Расчитывалось-то на машины с 8..32 мегабайтами памяти.
>Наверное, у тебя с выделением памяти проблема.
С новой Dll точно так же падал и DelphiQuake пока я не залез в свойства проекта и не увеличил там максимальный размер стера раз в восемь.
Сорцы (2 мегабайта):
http://chebmaster.com/_share/_001/_003/q_fclift_src/q_fclift_src.tgz
(upd. 2 декабря 2020: заменил ссылку на рабочую)
- там до кучи и весь Ку2, и вся Vampyre. Проекты под Turbo Delphi.
Я изменял только /ref_gl/gl_image.pas, и только две процедуры в ней.
Было
function GL_Upload32(data: PCardinal; width, height: integer; mipmap: qboolean): qboolean;
var
scaled: array[0..256 * 256 - 1] of Cardinal;
paletted_texture: array[0..256 * 256 - 1] of byte;
samples,
scaled_width, scaled_height,
miplevel: integer;
i, c: Integer;
scan: PByte;
comp: Integer;
label
done;
begin
uploaded_paletted := false;
scaled_width := 1;
while (scaled_width < width) do
scaled_width := scaled_width shl 1;
if (gl_round_down^.value <> 0) and (scaled_width > width) and (mipmap) then
scaled_width := scaled_width shr 1;
scaled_height := 1;
while (scaled_height < height) do
scaled_height := scaled_height shl 1;
if (gl_round_down^.value <> 0) and (scaled_height > height) and (mipmap) then
scaled_height := scaled_height shr 1;
// let people sample down the world textures for speed
if (mipmap) then
begin
scaled_width := scaled_width shr Trunc(gl_picmip^.Value);
scaled_height := scaled_height shr Trunc(gl_picmip^.value);
end;
// don't ever bother with >256 textures
if (scaled_width > 256) then
scaled_width := 256;
if (scaled_height > 256) then
scaled_height := 256;
if (scaled_width < 1) then
scaled_width := 1;
if (scaled_height < 1) then
scaled_height := 1;
upload_width := scaled_width;
upload_height := scaled_height;
if (scaled_width * scaled_height > sizeof(scaled) / 4) then
ri.Sys_Error(ERR_DROP, 'GL_Upload32: too big');
// scan the texture for any non-255 alpha
c := width * height;
scan := Pointer(Cardinal(data) + 3);
samples := gl_solid_format;
for i := 0 to c - 1 do
begin
if (scan^ <> 255) then
begin
samples := gl_alpha_format;
break;
end;
Inc(scan, 4);
end;
if (samples = gl_solid_format) then
comp := gl_tex_solid_format
else
if (samples = gl_alpha_format) then
comp := gl_tex_alpha_format
else
begin
ri.Con_Printf(PRINT_ALL,
'Unknown number of texture components %i'#10,
samples);
comp := samples;
end;
if (scaled_width = width) and (scaled_height = height) then
begin
if (not mipmap) then
begin
if (Assigned(qglColorTableEXT) and (gl_ext_palettedtexture.value <> 0) and (samples = gl_solid_format)) then
begin
uploaded_paletted := true;
GL_BuildPalettedTexture(@paletted_texture, PByte(data), scaled_width, scaled_height);
qglTexImage2D(GL_TEXTURE_2D,
0,
GL_COLOR_INDEX8_EXT,
scaled_width,
scaled_height,
0,
GL_COLOR_INDEX,
GL_UNSIGNED_BYTE,
@paletted_texture);
end
else
qglTexImage2D(GL_TEXTURE_2D, 0, comp, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
goto done;
end;
memcpy(@scaled, data, width * height * 4);
end
else
GL_ResampleTexture(data, width, height, @scaled, scaled_width, scaled_height);
GL_LightScaleTexture(@scaled, scaled_width, scaled_height, not mipmap);
if (Assigned(qglColorTableEXT) and (gl_ext_palettedtexture.Value <> 0) and (samples = gl_solid_format)) then
begin
uploaded_paletted := true;
GL_BuildPalettedTexture(@paletted_texture, @scaled, scaled_width, scaled_height);
qglTexImage2D(GL_TEXTURE_2D,
0,
GL_COLOR_INDEX8_EXT,
scaled_width,
scaled_height,
0,
GL_COLOR_INDEX,
GL_UNSIGNED_BYTE,
@paletted_texture);
end
else
qglTexImage2D(GL_TEXTURE_2D, 0, comp, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, @scaled);
if (mipmap) then
begin
miplevel := 0;
while (scaled_width > 1) or (scaled_height > 1) do
begin
GL_MipMap(@scaled, scaled_width, scaled_height);
scaled_width := scaled_width shr 1;
scaled_height := scaled_height shr 1;
if (scaled_width < 1) then
scaled_width := 1;
if (scaled_height < 1) then
scaled_height := 1;
Inc(miplevel);
if (Assigned(qglColorTableEXT) and (gl_ext_palettedtexture.Value <> 0) and (samples = gl_solid_format)) then
begin
uploaded_paletted := true;
GL_BuildPalettedTexture(@paletted_texture, @scaled, scaled_width, scaled_height);
qglTexImage2D(GL_TEXTURE_2D,
miplevel,
GL_COLOR_INDEX8_EXT,
scaled_width,
scaled_height,
0,
GL_COLOR_INDEX,
GL_UNSIGNED_BYTE,
@paletted_texture);
end
else
qglTexImage2D(GL_TEXTURE_2D, miplevel, comp, scaled_width, scaled_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, @scaled);
end;
end;
done: ;
if (mipmap) then
begin
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max)
end
else
begin
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
end;
Result := (samples = gl_alpha_format);
end;Стало
const
MAX_TW = 1024;
MAX_TH = 512;
var
scaled: array[0..MAX_TH * MAX_TW - 1] of Cardinal;
paletted_texture: array[0..MAX_TH * MAX_TW - 1] of byte;
function GL_Upload32(data: PCardinal; width, height: integer; mipmap: qboolean): qboolean;
var
samples,
scaled_width, scaled_height,
miplevel: integer;
i, c: Integer;
scan: PByte;
comp: Integer;
label
done;
begin
uploaded_paletted := false;
scaled_width := 16;
while (scaled_width < width*1.2) and (scaled_width <> width) do
scaled_width := scaled_width shl 1;
if width = 256 then scaled_width:=256;
scaled_height := 16;
while (scaled_height < height *1.2) and (scaled_height <> height) do
scaled_height := scaled_height shl 1;
// don't ever bother with >256 textures
if (scaled_width > MAX_TW) then
scaled_width := MAX_TW;
if (scaled_height > MAX_TH) then
scaled_height := MAX_TH;
if (scaled_width < 1) then
scaled_width := 1;
if (scaled_height < 1) then
scaled_height := 1;
upload_width := scaled_width;
upload_height := scaled_height;
if (scaled_width * scaled_height > sizeof(scaled) / 4) then
ri.Sys_Error(ERR_DROP, 'GL_Upload32: too big');
// scan the texture for any non-255 alpha
c := width * height;
scan := Pointer(Cardinal(data) + 3);
samples := gl_solid_format;
for i := 0 to c - 1 do
begin
if (scan^ <> 255) then
begin
samples := gl_alpha_format;
break;
end;
Inc(scan, 4);
end;
if (samples = gl_solid_format) then
comp := gl_tex_solid_format
else
if (samples = gl_alpha_format) then
comp := gl_tex_alpha_format
else
begin
ri.Con_Printf(PRINT_ALL,
'Unknown number of texture components %i'#10,
samples);
comp := samples;
end;
if (scaled_width = width) and (scaled_height = height) then
begin
if (not mipmap) then
begin
if (Assigned(qglColorTableEXT) and (gl_ext_palettedtexture.value <> 0)
and (samples = gl_solid_format)) then
begin
uploaded_paletted := true;
GL_BuildPalettedTexture(
@paletted_texture, PByte(data), scaled_width, scaled_height);
qglTexImage2D(GL_TEXTURE_2D,
0,
GL_COLOR_INDEX8_EXT,
scaled_width,
scaled_height,
0,
GL_COLOR_INDEX,
GL_UNSIGNED_BYTE,
@paletted_texture);
end
else
qglTexImage2D(GL_TEXTURE_2D, 0, comp, scaled_width, scaled_height,
0, GL_RGBA, GL_UNSIGNED_BYTE, data);
goto done;
end;
memcpy(@scaled, data, width * height * 4);
end
else
GL_ResampleTexture(data, width, height, @scaled, scaled_width, scaled_height);
GL_LightScaleTexture(@scaled, scaled_width, scaled_height, not mipmap);
if (Assigned(qglColorTableEXT) and (gl_ext_palettedtexture.Value <> 0)
and (samples = gl_solid_format)) then
begin
uploaded_paletted := true;
GL_BuildPalettedTexture(@paletted_texture, @scaled, scaled_width, scaled_height);
qglTexImage2D(GL_TEXTURE_2D,
0,
GL_COLOR_INDEX8_EXT,
scaled_width,
scaled_height,
0,
GL_COLOR_INDEX,
GL_UNSIGNED_BYTE,
@paletted_texture);
end
else
qglTexImage2D(GL_TEXTURE_2D, 0, comp, scaled_width, scaled_height,
0, GL_RGBA, GL_UNSIGNED_BYTE, @scaled);
if (mipmap) then
begin
miplevel := 0;
while (scaled_width > 1) or (scaled_height > 1) do
begin
GL_MipMap(@scaled, scaled_width, scaled_height);
scaled_width := scaled_width shr 1;
scaled_height := scaled_height shr 1;
if (scaled_width < 1) then
scaled_width := 1;
if (scaled_height < 1) then
scaled_height := 1;
Inc(miplevel);
if (Assigned(qglColorTableEXT) and (gl_ext_palettedtexture.Value <> 0)
and (samples = gl_solid_format)) then
begin
uploaded_paletted := true;
GL_BuildPalettedTexture(
@paletted_texture, @scaled, scaled_width, scaled_height);
qglTexImage2D(GL_TEXTURE_2D,
miplevel,
GL_COLOR_INDEX8_EXT,
scaled_width,
scaled_height,
0,
GL_COLOR_INDEX,
GL_UNSIGNED_BYTE,
@paletted_texture);
end
else
qglTexImage2D(GL_TEXTURE_2D, miplevel, comp, scaled_width, scaled_height,
0, GL_RGBA, GL_UNSIGNED_BYTE, @scaled);
end;
end;
done: ;
if (mipmap) then
begin
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_min);
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max)
end
else
begin
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gl_filter_max);
qglTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gl_filter_max);
end;
Result := (samples = gl_alpha_format);
end;Было
procedure GL_ResampleTexture(in_: PCardinal; inwidth, inheight: integer; out_: PCardinal; outwidth, outheight: integer);
var
i, j: Integer;
inrow, inrow2: PCardinal;
frac, fracstep: Cardinal;
p1, p2: array[0..1024 - 1] of Cardinal;
pix1, pix2, pix3, pix4: PByteArray;
tmp: PByteArray; // <- Added by Juha to ease some wicked pointer calculations.
begin
fracstep := (inwidth * $10000) div outwidth;
frac := fracstep shr 2;
for i := 0 to outwidth - 1 do
begin
p1[i] := 4 * (frac shr 16);
frac := frac + fracstep;
end;
frac := 3 * (fracstep shr 2);
for i := 0 to outwidth - 1 do
begin
p2[i] := 4 * (frac shr 16);
frac := frac + fracstep;
end;
for i := 0 to outheight - 1 do
begin
inrow := Pointer(Cardinal(in_) + (inwidth * Trunc((i + 0.25) * inheight / outheight)) * sizeof(Cardinal));
inrow2 := Pointer(Cardinal(in_) + (inwidth * Trunc((i + 0.75) * inheight / outheight)) * sizeof(Cardinal));
frac := fracstep shr 1;
for j := 0 to outwidth - 1 do
begin
pix1 := Pointer(Cardinal(inrow) + p1[j]);
pix2 := Pointer(Cardinal(inrow) + p2[j]);
pix3 := Pointer(Cardinal(inrow2) + p1[j]);
pix4 := Pointer(Cardinal(inrow2) + p2[j]);
tmp := Pointer(Cardinal(out_) + j * sizeof(Cardinal));
tmp[0] := (pix1[0] + pix2[0] + pix3[0] + pix4[0]) shr 2;
tmp[1] := (pix1[1] + pix2[1] + pix3[1] + pix4[1]) shr 2;
tmp[2] := (pix1[2] + pix2[2] + pix3[2] + pix4[2]) shr 2;
tmp[3] := (pix1[3] + pix2[3] + pix3[3] + pix4[3]) shr 2;
end;
Inc(out_, outwidth);
end;
end;
Стало
procedure GL_ResampleTexture(in_: PCardinal; inwidth, inheight: integer; out_: PCardinal; outwidth, outheight: integer);
var
i1, i2: TImageData;
begin
i1.width:=inwidth;
i1.height:=inheight;
i1.format:=ifA8R8G8B8;
i1.Size:=inwidth * inheight * 4;
i1.bits:=in_;
i2.width:=outwidth;
i2.height:=outheight;
i2.format:=ifA8R8G8B8;
i2.Size:=outwidth * outheight * 4;
i2.bits:=out_;
StretchResample(
i1, 0, 0, inwidth, inheight,
i2, 0, 0, outwidth, outheight,
sfLanczos, true);
end;Весь Quake2 на паскале? Круто =)
Запусти в отладчике. Если еще этого не сделал. :)
Скачиваю, сейчас гляну, как оно падает.
Не могу скачать -- FireFox молча отказывается.
>Весь Quake2 на паскале? Круто =)
А то.
Но начинатель этого проекта, увы, погиб в автокатастрофе. ДельфиКу2 при запуске выводит, что посвящён его памяти.
Кстати, доопёр, откуда чёрные полигоны. Если в уровень воткнут удаляемый источник света (кристалл там взрывающийся, или ещё что), то все полигоны, которые он освещает, освещаются только им. Где-то не прокатывает сложение.
>Запусти в отладчике. Если еще этого не сделал. :)
Ась?.. А что такое отладчик?..
Если серьёзно, всю жизнь использую debug writes, никогда отладчиками не пользовался. К тому же я сейчас под Виндовс набегами, разучился уже ей пользоваться - всё время на Линуксе сижу.
По идее, для разрешения всех проблем достаточно, чтобы кто-то владеющий Си, перекомпилировал в ней quake2.exe, в несколько раз подняв лимиты стека и кучи. Я уверен, этого будет достаточно.
>Не могу скачать -- FireFox молча отказывается.
???? :(
Может, антивирус шалит?
Или я в тот момент компьютер перезагружал.
Я тоже как-то давно разбирался с сорцами кваки. Изменил метод вывода частиц, теперь вместо обычных gl_points там текстурированные квады. Вот, если кому интересно:
http://www.gamedev.ru/download/?id=5787
Причем самое интересное, что в коде было почти все необходимое для этого, но многое было закомментировано. Текстура частиц там тоже была, но однобитная, я переделал ее в серошкальную. Интересно, что эта текстура (кругляшки) применялась движком в качестве дефолтной (т.е. когда оригинал не найден), а не по своему прямому назначению.
Cheb
Абалдеть - дело Jan Horn всеже живет.
Он долго и упорно начинал этот проект и прикладывал максимум сил, чтобы он развивался.
Светлая ему память.
6 June 2002 18:30
>>По идее, для разрешения всех проблем достаточно, чтобы кто-то владеющий Си, перекомпилировал в ней quake2.exe, в несколько раз подняв лимиты стека и кучи. Я уверен, этого будет достаточно.
Да не надо там ничего поднимать, просто буффер под картинку увеличить в 2 раза и все.
ИМХО, проблемы со стеком - чисто паскалевская особенность.
Сейчас попробовал ещё скачать длл - мой собственный сервер начал гнать какую-то пургу.
Похоже, длл файлы не любит.
Запаковал всё в архив:
http://host-17-99.imsys.net/_share/_001/_003/q2_facelifted.zip
>Да не надо там ничего поднимать, просто буффер под картинку увеличить в 2 раза и все.
Какой именно? И где он задаётся?
Напоминаю, что проблема с падением прямо при запуске присутствовала и в дельфи-версии екзешника, и решилась именно увеличением пределов стека/кучи в свойствах проекта - это параметры для линкера, сколько я знаю.
Поправьте если ошибаюсь, в Ку2 собственный диспетчер памяти и у него очень небольшой лимит.
Объём стека, насколько мне известно - это свойство, записываемое прямо в заголовке exe файла, поскольку под стек системе нужно резервировать непрерывный и линейный кусок виртуального адресного пространства, чтобы ненароком не выделить в нём страницу под кучу. Соответственно, стек не может вырасти больше, чем на определённую величину.
>Я тоже как-то давно разбирался с сорцами кваки.
Вот собраться бы, да свести свои усилия воедино...
У меня где-то и VC и сишные исходники Ку2 лежат - но сколько сил нужно, чтобы всё это откопать, установить и снова построить - просто руки опускаются.
Тема в архиве.