Open DileSoft opened 2 years ago
Давайте рассудим всё основательно. Разделим формальные признаки (сам код) от неформальных (все остальные соображения).
Что было бы логичнее? С точки зрения наименования "delta" - это больше похоже на расстояние между двумя пластами земли, она же высота пещеры.
Что говорят авторы? По сообщению Андрея Кузьмина, была проблема:
я только помню, что с этими потолками была проблема, задевали крупные мехосы и в целом у меня отложилось подозрение, вто с ними что-то не так, но времени уже не было тогда разбираться. возможно, там как раз баг. круто если он будет наконец исправлен! :)
По сообщению Александра Котляра, это толщина:
толщина верхнего слоя конечно. По нижнему мы ездим, а толщина потолка практически нигде не фигурирует.
Как смотрится лучше? Похоже, что с толщиной смотрится лучше.
Результат - смешаный.
Давайте покопаемся в коде внимательно:
1) Начнём с Surmap, функция WORLD_handle()
hd = *pa;
hu = *(pa + 1);
if((h = hu - hd) < 32){
SET_DELTA(*pf,0);
SET_DELTA(*(pf + 1),0);
}
else {
h -= 16;
if(h > MAX_RDELTA) h = MAX_RDELTA;
h = (h >> DELTA_SHIFT) - 1;
SET_DELTA(*pf,(h & 12) >> 2);
SET_DELTA(*(pf + 1),(h & 3));
}
}
Здесь описана следующая логика: если расстояние между верхним и нижним слоем меньше 32, отнять 16 и закодировать её в дельте. Это похоже именно на высоту пещеры.
2) Продолжим с Surmap, функция AcceptSecondLayer
h = GET_UP_ALT(pf,*pa,pa0,x);
hy = GET_UP_ALT(pfy,*pay,pay0,x);
d = abs(h - hy);
if(d >= TunnelHeight){ ...
d = (d >> DELTA_SHIFT) - 1;
...
SET_DELTA(*pf,(d & 12) >> 2);
SET_DELTA(*(pf + 1),(d & 3));
...
}
Опять же, разница между уровнями записывается в d
, сравнивается с некой константой TunnelHeight
(практичекси дословно "высота пещеры"), а потом записывается в нашу дельту. Так что тут совершенно точно высота пещеры.
3) подземные эффекты. Например, MakeSecondLevel:
int d = 32;
Похоже, во всём модуле полагается дельта равная 32. К какой семантике больше подходит? Не ясно.
4) Неподалёку лежит файл "hobj.cpp" с функцией GetCollisionMap:
d = *(p -1);
if(d < z && (d + (((GET_DELTA(*(t - 1)) << 2) + GET_DELTA(*t) + 1) << DELTA_SHIFT)) > z) return 0;
Тут вычисляется пересечение точки с ладшафтом. Совершенно ясно, что берётся трактовка дельты как высоты пещеры.
5) идём дальше - в код модификации земли, pixDownSet. В модуле есть много упоминаний от том, как дельту складывают с нижним слоем:
if((int)*(pa - 1) + (((GET_DELTA(*(pf - 1)) << 2) + GET_DELTA(*pf) + 1) << DELTA_SHIFT) >= h){
Но с уверенностью я сказать не решусь, что происходит в этом модуле.
6) переходим к 3Д физике в файле "dynamics.cpp". Интересное объявление:
#define GET_THICKNESS(p) ((GET_DELTA(*HIGH_LEVEL((p) + H_SIZE)) + (GET_DELTA(*LOW_LEVEL((p) + H_SIZE)) << 2) + 0) << DELTA_SHIFT)
Вычисляется всё та же дельта, но результат называют "thickness", что могло бы означать толщину верхнего слоя.
Но анализ реального использования этого макроса говорит о другом. Вот get_three_heights
:
uintptr_t ll = *LOW_LEVEL(p);
return ll | (ll + (GET_THICKNESS(p) << 8)) | ((uintptr_t)(*HIGH_LEVEL(p)) << 16) | 0xff000000;
Функция возвращает 3 отметки высоты: первый уровень, начало второго уровня, конец второго уровня. Явно видно, что "thickness" не вычитается из второго уровня, а прибавляется к первому. Это значит, что настоящий смысл данного макроса - вернуть высоту пещеры.
Кроме того, это похоже на реальный баг. Смотри #610 с предлагаемым исправлением.
7) Последняя остановка нашего путешествия (а я полагаю, что ничего не пропустил) - код отрисовки "render.h". Начнём с до боли знакомой обёртки GET_WIDTH:
inline uchar GET_WIDTH(uchar* type, int x)
{
if(*type & DOUBLE_LEVEL){
if(x & 1)
return (((GET_DELTA(*(type - 1)) << 2) + GET_DELTA(*type) + 1) << DELTA_SHIFT);
else
return (((GET_DELTA(*type) << 2) + GET_DELTA(*(type + 1)) + 1) << DELTA_SHIFT);
}
return 0;
}
Итак, она возвращает нашу закодированную дельту, также как и GET_THICKNESS
в другом месте. Любопытно, что она используется только в одном месте - отладочном (нефункциональном) коде surmap:
t = GETWIDTH(CX,CY);
status < "x:" <= CX < " y:" <= CY < " col: " <= *(vMap -> lineTcolor[CY] + CX);
status < " type: " <= GETTERRAIN(CX,CY);
if(dl) status < "/" <= GETDOWNTERRAIN(CX,CY);
if(!dl)
status < " alt:"<= u;
else {
d = GETDOWNALT(CX,CY);
status < " d:" <= d;
status < " u:"<= u;
status < " t:" <= t;
status < " w:" <= (u - d - t);
}
Отсюда не ясно, что оно значит. Более того, похоже, что значение менялось. GETWIDTH
присваивается переменной t
, которая вероятно изначально принимала "thickness", а потом вычисляется некий "w:" на основе этого (вероятно, "width").
Есть ещё такая штука в этом же файле:
const int DELTA = 16;
Она не похожа на нашу дельту, используется везде. Скорее, это просто квант изменения любых высот в данном контексте.
8) как же всё-таки рисуется? Полагаю, дельта для отрисовки не используется вовсе. Просто рисуется нижний и верхний слои. Если кто-то сможет раскопать тут по-лучше - буду рад помощи. Но в такой интерпретации это и не глубина пещеры, и не толщина слоя.
Неформальные доводы смешаны. Формальные доводы в виде кода оказались практически непротиворечивыми - из всех 8-ми мест использования дельты, 5 оказались в значении высота пещеры и ни одного, конкретно указывающего на толщину.
В коде явно что-то менялось, и вполне возможно, что раньше кодировалась именно "thickness". Возможно, Котляр этот момент и вспомнил. Но в той версии, что у нас есть сейчас, всё прямо указывает на высоту пещеры, и логика surmap в этом согласна с 3Д физикой и всем остальным. На случайную ошибку не похоже.
У меня есть возражения.
Во-первых, я исхожу из того, что игру пишут не ради кода, а ради конечного результата. Соответственно, если мир выглядит искаженным, то это явно неправильное понимание кода. Тут есть и чисто практическая причина. Очевидно, когда разработчики рисовали мир, они рисовали провода прямымим, проемы под мостами широкие и т. д. В том числе просто вставляя 3d-модели в карты высот. Предположить что они во всех мирах везде одинаково ошиблись, маловероятно. Поэтому очевидно, что миры рисовались исходя из того, что delta это толщина верхнего слоя.
Тени, которые (хотя это надо бы проверить) рисуются исходя и из ширины, и из толщины верхнего слоя, показывают провода прямыми. Если бы они рисовались исходя из дельты-пещеры, они бы были вытянутыми.
Во-вторых, как я понял, ты просто почитал код, а я проверял, для чего он используется и где выполняется. Теперь подробнее:
Я согласен с тем, что код противоречив. Но визуально все выглядит так, что когда рисовали миры, дельту считали толщиной. Дальше действительно непонятно что происходило.
Проблема в том, что когда мы начинаем рисовать миры с боку из позиции что дельта это высота пещеры, мы получаем уродливые миры. Когда считаем что толщина верхнего слоя, миры выглядят нормально.
И дело не только в эстетике. Если считать физику исходя из высот, и считать дельту высотой пещеры, мы, проезжая под проводом на берегу реки, и въезжая под ним в реку, должны уткнуться в этот провод, которые начинает удлиняться резко вниз, что выглядит странно. Если в коде высота пещеры фиксированная, то все равно уткнемся. Если толщина верхнего слоя фиксированная, не уткнемся.
Отсюда мой вывод, что уровни и визуально, и физически могут работать только если мы считаем дельту толщиной верхнего слоя или считаем толщину верхнего слоя фиксированной.
Попробовал на потрошители проехать под проводом на берегу и заехать в воду.
Мехос наткнулся на препятствие и наклонился вниз как будто провод реально вытянулся вниз.
Выглядит будто реально работают то ли с дельтой, то ли с константой как высотой пещеры.
Короче физика у нас противоречит логике.
Проверил еще в сюрмапе.
Начал наращивать верхний слой. w выросло, t осталось на месте.
Начал наращивать нижний слой. t и w в процессе попеременно то увеличиваются то уменьшаются.
Но судя по верхнему слою, t реально высота пещеры, а w наоборот толщина верхнего слоя.
Получается, ты прав и сюрмап тоже так считает.
Я признаю, что везде сейчас дельта это высота пещеры.
Но это приводит и к кривому визуалу с вытягивающимися проводами и толстыми мостами, и к кривой физике, где ты натыкаешься на вытянутые провода над рекой.
Короче, дельта = пещера это канонично, но выглядит и играется неправильно.
Остался правда вопрос с тенями. Но как они считаются, понять невозможно.
Сюрмап понимает дельту как толщину верхнего слоя, и, судя по всему, так и предполагалось изначально:
https://github.com/KranX/Vangers/blob/5a214b175fc29dfa750b1c1e71cbbd81aa1d00bb/src/terra/render.h#L109-L118
При рендеринге в 3d провода выглядят нормально:
Физика расчета пуль понимает дельту как высоту пещеры:
https://github.com/KranX/Vangers/blob/5a214b175fc29dfa750b1c1e71cbbd81aa1d00bb/src/units/hobj.cpp#L1555-L1564
При рендеринге в 3d провода вытянуты вниз:
Физика расчета 3d объектов вообще игнорирует дельту и использует фиксированную ширину верхнего слоя:
https://github.com/KranX/Vangers/blob/5a214b175fc29dfa750b1c1e71cbbd81aa1d00bb/src/3d/dynamics.cpp#L67 Визуализации в 3d третьего способа нет.