Из журнала Adventurer#13,
Ярославская область, г.Рыбинск, ?


      Research/Volgasoft
 
      Как написать 3D игру типа DOOM
 
     В  этой  статье я собираюсь поведать
(ну и показать рабочий пример, естествен-
но)  о  том,  как  сделать (как делались)
трехмерную  гаму на 3d движке с секторной
организацией.  В  свое  время  Доктор STS
(теперь  более известный как Доктор Луже-
ная Глотка :))) ) занимался этим вопросом
и  написал пару не сильно отличавшихся 3d
движков  (code  name:  ZX  Duke Nukem). В
принципе,  об аналогах я не слышал, может
кто-то  и  сделал  что-то подобное, а раз
таковых нет, то будем рассматривать един-
ственный  существующий экземпляр. Дабы не
дать издохнуть шедевру, я приспособил его
в  дему,  выпущенную  под названием <Love
Gun>.  Если Вы ее видели, значит поймете,
о чем идет речь. Сразу хочу сказать огро-
мное  спасибо другому известному <отцу> -
дяде  Darkу  из ныне усопшего X-Trade. Он
тоже в огромной степени причастен к напи-
санию движка, как и STS. В принципе, нем-
ного посодействовал материалами и аппара-
тными  средствами и я. Все, что здесь бу-
дет  затронуто,  базируется на материалах
двух   вышеупомянутых  "трехмерных  пап",
опубликованых в электронном журнале "Spe-
ctrum  Expert".  Начните с него, иначе ни
фига не поймете.
     Итак, приступим:
 
     В  начале  рассмотрим, что же это за
секторная организация такая.
 
     Под  секторами  будем понимать некую
замкнутую трехмерную фигуру, из любой то-
чки которой беспрепятственно видны другие
точки  этой фигуры. Грубо говоря, это две
сваи, одна из которых вбита в пол, а дру-
гая  в потолок, и все это оклеено обоями,
а мы внутри оставшегося между сваями про-
странства. Сектор описывается координата-
ми  точек, из которых он состоит, высотой
(координатой) потолка, высотой (координа-
той)   полов,   схемой  соединения  точек
(стен), ну и естественно, номеров заливок
каждой  стены  (грани),  полов и потолка.
Вот кусочек карты LoveGun.
     Итак, имеем уровень, разбитый на се-
ктора.  Как выглядят сектора, Вы уже, на-
верное, догадались. Смотрим на сектор S0.
Он    состоит   из   точек   с   номерами
0,1,2,3,4,5,6,7,8,9,0. Точки соединены по
часовой стрелке и образуют стены: 0-9, 9-
8,  8-7 и т.д. Набросав такой восьмигран-
ник,  мы получим возможность по нему пок-
рутиться. Но: встает проблема, а че даль-
ше?  На  одном секторе игру не построить.
Даже на двух или десяти. Чего от них тол-
ку,  если  это  независимые друг от друга
помещения?  Ведь их как-то сцеплять надо.
Вот  эти  проблемы и выделены на картинке
жирными линиями. Называются они межсекто-
рными  соединениями.  Итак,  наши сектора
имеют  общую грань - 0-9 для S0 и 9-0 для
S1.  А это означает большое ведро гвоздей
в заднице в плане кода и вычислений. При-
мер  -  когда  я  делал  подъем  (сектора
S4-S9), то хотел сделать его гладеньким и
впиндюрил 15 ступенек. Скорпион в резуль-
тате повис в жопу, эмулятор Шалаева выжи-
мающий  300 тысяч тактов за frame (на Cy-
rix200)  решил,  что тут на спектруме mp4
показывают.  Подумав, что это все-таки не
глюк, я обошелся пятью ступеньками. <Нау-
чное> объяснение сего будет ниже.
 
     Переключаемся  на  исходник  <мирка>
урезанного    под   наш   кусочек   карты
(world5.c).
 
     Таблица указателей на сектора:
 
SECTB
 _DW S0,S1,S2,S3,S4,S5,S6,S7,S8,S9
 
     Указатели располагаются в том же по-
рядке, в котором лежат сами сектора.
 
     А  вот  и  сектора. Мне понадобилось
какое-то  время, чтобы расрюхать чего там
STS понаделал:
 
 ORG $^
 SECTORS
 ;FLOOR'z,CIELING'z
 ;SLOPE FLOOR,SLOPE CIELING
 ;HITAG,LOTAG,EXTRA
 ;FLOORPIC,CIELINGPIC
 
 ;пример сектора:
 S0      DW 300,-400,0,0,0,0,0:DB 22,15
 ; шапка сектора
 ;DW высота потолка, пола, потом
 ;незадействованные поля (под всякие
 ; спецэффекты), потом:
 ;DB заливка полов, потолка
 ;кстати - потолок <внизу>
 
 ;Затем идет список узлов, из которых состоит
 сектор
 ; POINT, FIL, NUM, CONNECT, SEC
 ;DW номер точки: DB заливка стены <этот узел -
 ;следующий за ним>, номер данной стенки,
; номер стенки общей с соседним сектором
 ;(если 0 - стена не прозрачная) (<сцепка>),
 ; номер сектора с которым происходит сцепка
 ; (-1 - нет соединения).
 
         DW 9:DB 1,0,0,-1
         DW 8:DB 14,1,0,-1
         DW 7:DB 6,2,0,-1
         DW 6:DB 12,3,0,-1
         DW 5:DB 20,4,0,-1
         DW 4:DB 12,5,0,-1
         DW 3:DB 6,6,0,-1
         DW 2:DB 14,7,0,-1
         DW 1:DB 1,8,0,-1
         DW 0:DB 25,9,12,1
 ;соединяемся с 12-м
 ;ребром сектора S1
 
         DW -1; конец сектора,
 ;соединение 0-го и 9-го узлов
 
 ;пример второго сектора
 S1
 _DW 300,-400,0,0,0,0,0:DB 14,19
 
 _DW 12:DB 13,10,14,2; соединяемся
 ;с 14-м ребром сектора S2
         DW 10:DB 27,11,0,-1
 _DW 9 :DB 4,12,9,0; соединяемся с
 ;9-м ребром сектора S0
 
         DW 0 :DB 27,13,0,-1
         DW -1; конец сектора,
 ;соединение 0-го и 12-го узлов
 
     А вот и сами точки из которых клеят-
ся сектора:
 
ORG $^
 ;DW X,Y; X=-16384,16383 Y
 ;тоже, если мне не изменяет
 ;память
 DOTS    DW 5500,4000;0 S0
         DW 5000,4000
         DW 4000,3000
         DW 4000,1000
         DW 5000,0
         DW 7000,0
         DW 8000,1000
         DW 8000,3000
         DW 7000,4000
         DW 6500,4000;9
 
         DW 6500,6500;10 S1
         DW 5000,6500;11
         DW 5500,5500;12 S2
         DW 5000,5500;13
 
     Как  было  уже  отмечено выше, такая
идея  организации позаимствована с писюка
и естественно подправлена под возможности
спектрума.
 
     С  устройством  секторов все. Теперь
переключимся на то, как это все заливает-
ся.
 
     Сейчас  я  Вам  продемонстрирую пару
картинок:
 
     Эту  можно назвать <Глазами зрителя>
     А эту <Истина>
     Видите?  Ну, каково, не передернуло?
А  все дело в том, что друг наш спекки не
умеет  быстро  считать.  Да,  да:. Залить
стенку  целиком  от пола до потолка полу-
чается  быстрее,  чем обсчитывать участок
действительно  видимый  зрителю,  и затем
заливать  его,  причем обычно в два раза.
Обдумав  эту  картинку,  STS понял, что в
принципе  для  ускорения  движка, заливок
потребуется две: первая - обычная (проце-
дура fdrpoly), вторая - заливка прямоуго-
льников  от  пола до потолка - специально
для стен (процедура filv). Кстати она це-
ликом  стековая и ускорению фактически не
подлежит.  Про заливки остается добавить,
что  они заливают по столбцам - так быст-
рее, но вынос буфера на экран вида:
 
_POP BC: LD (HL),C: INC H:LD (HL),B:
INC H
 
жрет  порядочно  быстродействия.  Это  не
 
LD HL,#0000: PUSH HL
 или POP HL: LD (SCREEN),HL
 
какой-нибудь. С другой стороны - это неи-
збежность.  Вот  поэтому-то 15 ступенек и
не получилось.
 
      Подводим небольшой итог.
 
     Итак, обработчик секторов использует
самые  прогрессивные  на сегодняшний день
процедуры  для  обсчета (с использованием
логарифмов, примерных вычислений и т.д.).
К  тому же, в нем задействована рекурсия.
Посему  он  оптимизации почти не подлежит
(скорее,  он  подлежит  только  дописке).
Другое  дело блок заливок. Заливки, как и
сортировки,  всегда  были  <русским полем
оптимизации>.  Ведь на мелкие деталюшечки
столько тратится! Было бы, наверное, пра-
вильнее сделать не две, а все три или бо-
лее заливок. Сейчас у меня вертится в го-
лове  одна  идея, тока не знаю, насколько
она реализуема, посему пока промолчу.
 
     Была,  помнится,  идея  сделать этот
движок проволкой. В принципе, а почему бы
и  нет? Вопрос только в невидимых линиях,
а это опять математика.
 
     На  последок опять вернемся к исход-
нику.
 
     Я  оставляю всe as is. Обычно STS не
разбивал  программы  на логические модули
(в  шторме  с  ними работать не больно-то
удобно),  а  напихивал столько, сколько в
текстовую  банку  влазит. Я позволил себе
наглость  нарисовать  некоторые коммента-
рии. Разобраться с тем, что там делается,
дело, наверное, недели (у меня ушло в це-
лом дня четыре).
     Еще  подбрасываю библиотеку GRAFLIB,
составленную  STS'ом из  процедур Дарка и
своих  собственных. Она пригодится любому
3D <писателю>.
     Разжевывать  дальше  не имеет смысла
=> на этом пожалуй все.