Из журнала ZX Power #3, Харьков, 1998 Быстрая 3D-графика для SPECCY, реально ли это? Возможно, нижеследующая статья раскроет для Вас ранее неизвестные воз- можности SPECCY и окажется полезной при работе с трехмерными объектами. Статья расчитана на более-менее подготовленных читателей, но может оказаться полезной даже начинающим пользователям... (C) 1997 BY RUFF OF AVALON/RUSH/ASM _________________________________________ Прочитал я как-то статью в "ECHO 1" про вывод 3-D объектов. Там описан тради- ционный метод, который все (кто знаeт) используют. Это, конечно, очень интересно - поворачивать точки вокруг осей X, Y и Z по специальным формулам. Для поворота вокруг оси X: Y'=COS(N)*Y-SIN(N)*Z Z'=SIN(N)*Y+COS(N)*Z вокруг оси Y: X'=SIN(N)*Z+COS(N)*X Z'=COS(N)*Z-SIN(N)*X вокруг оси Z: X'=COS(N)*X-SIN(N)*Y Y'=SIN(N)*X+COS(N)*T (N - Угол поворота) У этого способа есть недостатки. А ес- ли точнее, один очень большой недостаток - занимает много времени! На операции ум- ножения (какими б они быстрыми не были) и вычисления COS и SIN (даже по таблице) уходит немало времени. Если учесть проблему со знаками и то, что обычно объекты вращают сразу вокруг трех осей (некоторые вообще используют калькулятор BASIC'а), то тут ничего не остается, кроме как сперва рассчитывать координаты, а потом выводить graphix. Не- которые даже подгружают отдельно рассчи- танные на BASIC'е координаты. Это же la- mer'ство! (Кроме отдельных случаев) Правда, иногда встречаются программки с REAL-TIME 3D CALCULATING'ом. Но как они тормозят!!! ...Это было вступление. Теперь ближе к делу. Я придумал новый способ вывода 3D объектов. Вот его достоинства: - Он намного (!!!) быстрее вышеописанно- го способа. Чтобы вычислить координаты одной точки, нужно приблизительно 200 тактов (при желании можно сделать еще быстрее) - 3D объект можно не только поворачи- вать, но и деформировать (растягивать, сжимать и т.д.) Короче, скоро выйдет наша (AVA- LON'oвская) демка (а может и не выйдет (а, скорее всего, уже вышла)), в которой будет 3D эффектик с ROTATING'ом человеч- ка. Человечек состоит из 175 точек (если точнее, то из 175 векторов, правда, на человечке это не очень заметно, так как руки и ноги у него сравнительно ровные). Если б он состоял из линий, то эффект не влез бы в одно прерывание (точки ставить быстрее). Это, конечно же, мой REALTIME 3-D ROTATOR. Я не могу полностью привести здесь листинг этого эффекта, поэтому привожу все по кускам, которые вы сами соберете и дополните кому что надо. Вот первый кусок - сама процедура печати 3D объекта: PRINT LD (SAV+1),SP ; сохраняю SP LD SP,... ;... - Это начальный адрес координат то- ;чек 3D объекта. На одну точку 4 байта (3 ;байта - координаты X,Y,Z; 1 байт старший ;байт таблицы осей (про нее чуть попозже) ;он, кстати, дальше обозначается меткой ;TABAX). EXX LD HL,BUFER ;специальный буфер для ;очистки экрана (смотри ;ниже) LD B,175 ;кол-во точек LP1 ;дальше идет процедура печати одной точки EXX POP BC ;В BC - Y,X POP HL ;В HL - старш. байт таблицы ;осей, Z LD A,(HL) LD E,L LD L,B INC H ADD A,(HL) INC H LD L,C ADD A,(HL) ADD A,HX LD C,A INC H LD A,(HL) LD L,E INC H ADD A,(HL) LD L,B INC H ADD A,(HL) ADD A,LX LD L,A ;теперь координаты точек (X и Y на экра- ;не) вычислены и помещены в регистры C и ;L. дальше идет процедура печати точки по ;координатам в этих регистрах (69 тактов) ;для нее нужна специальная таблица, так ;что, кто такой процедурки не знает, мо- ;жет использовать свою. PLOT LD H,TABLE_P LD D,(HL) INC H LD A,(HL) INC H LD L,C ADD A,(HL) INC H LD E,A LD A,(DE) OR (HL) LD (DE),A ;ну, вот. Точку напечатали, теперь можно ;(если хотите) в специальный буфер помес- ;тить адрес в экране этой точки (для то- ;го, чтобы потом быстро очистить экран ;методом POP HL: LD (HL),0: POP HL: LD ;(HL),0: POP HL...) LD A,D EXX DEC HL ;адрес буфер растет вверх ;ногами (для удобства) LD (HL),A EXX LD A,E EXX DEC HL LD (HL),A DJNZ LP1 ;следующая точка SAV LD SP,0 RET Как-то непривычно смотрятся команды LD SP,0: RET. Зато экономит память. Вы, надеюсь, догадались, что эта про- цедура сама по себе ничего не даст. Еще нужно написать процедуру очистки экрана (look up) и процедуру создания таблиц осей. Здесь я их не привожу по причине большого объема (длины, массы), который они занимают. Очистку экрана я, конечно, мог бы написать, только насчет этого у каждого свои принципы (кто бы написал installer, кто поместил бы все в dup'ы, и т.д., и т.п.), короче, сами напишите. А насчет создания таблиц с координата- ми осей, то лучше я сперва расскажу прин- цип. Y рисунок 1 ________________ /!^ ! / !* ! / !>48 .А ! / !* ! ! !* ! ! !* ! ! !* ! ! !* ! ! !* ! ! !* ! ! !* 32 ! ! !*______^________! ! 30/****************>/ X ! ^/* / ! /* / !/<________________/ Z Посмотрите на рисунок 1. Как вы догадались, (а может и нет) это куб. Представьте, что внутри него нахо- дится 3D объект. Каждая точка этого об- ъекта имеет три координаты по осям Y, X и Z. Здесь оси не бесконечные. Например, в моем эффекте они состоят из 64 точек (ко- ординат). То есть, точку можно задать тремя числами от 0 до 64. А теперь самое интересное. Возьмем, например, точку а с координа- тами Y=48, X=32, Z=30. Как узнать ее ко- ординаты в экране, если учесть, что ось Z повернута так, как на рисунке? На каждой оси помечена точка с соот- ветствующей координатой. Главное, нужно узнать координаты Y и X этих точек и центра (точка пересечения осей) на экра- не. Допустим, мы их знаем: координаты от левого верхнего угла Y, X НА ОСИ Y-70,46 НА ОСИ X-172,140 НА ОСИ Z-180,40 ЦЕНТР -172,46 Чтобы вычислить координаты в экране точки "A" я придумал (может их и до меня кто-то придумал) две простые формулы: Обозначим координаты в экране (то же, что и выше, только вместо цифр буквы): НА ОСИ Y-Yy,Xy НА ОСИ X-Yx,Xx НА ОСИ Z-Yz,Xz ЦЕНТР -Yc,Xc Итак, формулы: Y= (Yy-Yc)+(Yx-Yc)+(Yz-Yc)+Yc X= (Xy-Xc)+(Xx-Xc)+(Xz-Xc)+Xc Подставляем значения: Y =(70-172)+(172-172)+(180-172)+172=78 X =(46-46)+(140-46)+(40-46)+46=134 Координаты точки "A" в экране 78,134. Вышеприведенная программка печати 3D объекта именно это и делала - подсчитыва- ла сумму (правда, немного по другому (lo- ok down)), печатала точку и запоминала адрес в экране. А что же должна делать программа рассчитывания координат осей? Конечно, рассчитывать координаты осей. Но давайте сперва разберемся с форму- лами. Вы, наверное, уже давно пораскрыва- ли скобки, посокращали лишние координаты центра и уверены, что я это не заметил. Так можно сделать, но в этом мало смысла. Вот в чем дело: тут от каждой координаты точки оси отнимается координата центра. Вернемся к координатам точек осей. Как вы догадались, придется рассчитывать коорди- наты всех 64 точек осей. А эти координаты мы будем рассчитывать не так, как они есть в экране, а с центром с координатами в экране 0,0. А потом останется только прибавить координаты центра. Сама процедура рассчитывания коорди- нат осей занимает не много времени. У ме- ня, например, (если процедуру поставить в начале прерывания) кадровая развертка не успеет дойти даже до первых строк экрана, то есть, полностью находится на бордюре. Но для координат осей нужен буфер. Посчитаем его длину: одна ось - 64 точки, то есть 128 координат. всего осей 3, значит, всего координат 128*3=384 ко- ординаты. Одна координата - один байт, 384 координаты - 384 байта. Но ради ускорения вывода 3D спрайта, лучше координаты одной оси хранить в 512 байтах (координаты Y-256 байт, координаты X-256 байт. Из 256-ти байт используются только 64). Всего буфер выйдет 512*3=1536 (байт). Тоже не очень много. Ну, а теперь сама программа. Принцип работы будет объясняться по ходу дела. Как уже писалось выше, мы будем рассчиты- вать координаты осей так, если бы центр имел координаты 0;0. А потом уже, при вы- воде, прибавим координаты центра. Это сэ- кономит время. Кстати, координаты центра в моей программке будут храниться в ре- гистровой паре IX, так что ее содержимое не меняйте, или сохраняйте, а потом восстанавливайте (моя программа рассчиты- вания координат осей связана с программой печати 3D спрайта парой IX и содержимым буфера координат осей). AX ; Процедура рассчитывания координат ;осей. Координаты (в экране) крайних ;точек осей (на рис.2 эти точки обоз- ;начены буквами Ky, Kz И Kx) задаются ;в регистрах HL', BC, HL. координаты ;центра в DE. кстати, для тех, кто не ;хочет, чтобы 3D объект сжимался и ;растягивался, то может для поворота ;этих 4-х точек использовать формулы ;3-D вращения (look up). TABAX EQU 250 (64000=250*256) ;Старший байт буфера, в который рас- ;считываются координаты осей. Адрес ;буфера кратный 256 (!!!) LD A,E SUB 32 LD E,A LD A,L SUB 32 LD L,A LD A,C SUB 32 LD C,A EXX LD A,L SUB 32 LD L,A EXX LD HX,D ;Имеется в виду ст.байт IX. LD LX,E ;Младший байт IX. ;(недокументированные ко- ;манды процессора Z-80. 6 ;тактов) LD A,L PUSH AF LD A,C PUSH AF EXX LD A,L PUSH AF EXX LD A,H PUSH AF LD A,B PUSH AF EXX LD A,TABAX LD (VAR),A LD A,H LD B,HX CALL K64 POP AF LD B,HX CALL K64 POP AF LD B,HX CALL K64 POP AF LD B,LX CALL K64 POP AF LD B,LX CALL K64 POP AF LD B,LX CALL K64 RET K64 ;К64 - самая основная процедура. Вызы- ;вается программой AX 6 раз. Просчитывает ;промежуточные 64 координаты.Координата - ;цель в регистре A,а источник в B. Источ- ;ник у нас центр, значит в B всегда коор- ;дината центра (Y или X). А вообще здесь ;она нужна только для получения разницы ;(A-B или B-A) и для определения направ- ;ления, (положительное или отрицательное) ;так как координаты центра у нас 0;0 ;(look up). ; Адрес таблицы в 64 байта (куда помес- ;тить все числа) кратный 256. Его старший ;байт задается по адресу VAR. Этот байт, ;кстати, программка в конце сама увеличи- ;вает на 1. ; Так, я рассчитывал сперва координаты ;Y, вызывая эту процедуру, а потом X-ы, ;вызывая ее еще раз. Осей у нас три, ;поэтому процедура K64 вызывается 6 раз. CP B JP C,LOOP1 SUB B LD L,A LD H,0 ADD HL,HL ADD HL,HL EX DE,HL LD HL,0 LOOP2 EXX LD A,(VAR) LD H,A INC A LD (VAR),A LD L,0 EXX DUP 64 ;Или какой-нибудь цикл, типа ;LD B,64:...: DJNZ ... LD A,H ADD HL,DE EXX LD (HL),A INC L EXX EDUP RET LOOP1 LD C,A LD A,B SUB C LD L,A LD H,0 ADD HL,HL ADD HL,HL EX DE,HL LD HL,0 LOOP3 EXX LD A,(VAR) LD H,A INC A LD (VAR),A LD L,0 EXX DUP 64 SBC HL,DE LD A,H EXX LD (HL),A INC L EXX EDUP RET CP B JP C,LOOP4 SUB B LD L,A LD H,0 ADD HL,HL ADD HL,HL EX DE,HL LD H,B LD L,0 JP LOOP2 LOOP4 LD C,A LD A,B SUB C LD L,A LD H,0 ADD HL,HL ADD HL,HL EX DE,HL ADD A,C LD H,A LD L,0 JP LOOP3 VAR NOP NOP Ну, вот и все. Для тех, кто не вник в эти все дела или не хочет дополнять (из- менять, совершенствовать) мою 3D идею, a хочет написать такой эффект, как мой: 1. Не забудьте про формат хранения ко- ординат 3D oбъекта. Не забудьте про буфе- ры (координат осей, delete buffer). 2. Координаты крайних точек осей (Kz, Kx, Ky и центр) считайте по формулам вра- щения или по синусоидной табличке (у ме- ня, кстати, есть крутой способ генериро- вания син. табличек. таблички рассчитыва- ются гораздо быстрей и удобнее, чем на бейсике и компрессируются где-то в 20 раз. моя табличка в килобайт заняла меньше 50 байт. И декомпрессировать ее можно по ходу дела. Займет это около 100-200 тактов в прерывании. В ближайшее время я про это все тоже что-нибудь напи- шу). 3. Алгоритм программы вывода должен быть где-то таким: EI HALT DI CALL PRINT Заносим в hl', bc, hl, de следующие значения из син.таблицы (нужно еще учесть, чтобы они не были слишком больши- ми, а то спрайт может вылезти за экран и залезть с другой стороны (рис.3)). CALL AX Очистка экрана с использованием delete buffer'а (look up). Желательно приспосо- бить для работы с двумя экранами (у кого 128кб, конечно). JP BEGIN THAT WERE ALL I WANTED TO TELL U. I WISH U SUCCESS. SO LONG ! . . . Прочитав эту статью нашего кодера RUFF'а, я (VIATOR), решил внести некото- рые коррективы в текст, для прояснения определенных нюансов... Во первых, должен сказать, что тут описан по-настоящему выдающийся способ работы с 3D объектами в реальном времени. Для тех, кто не понимает данного выраже- ния, скажу - REALTIME-программы и эффекты - это те, которые рассчитываются по ходу работы программы, а не просчитываются за- ранее. Часто под этим выражением ошибочно подразумевают, что программа работает за одно прерывание, что не имеет ничего об- щего с REALTIME и называется ONE INT, или ONE FRAME (одно прерывание/один кадр). Преимущество программ в реальном времени заключается в экономном использовании па- мяти и гибкости структуры, например можно заставить двигаться трехмерный объект не по заранее фиксированной траектории, а по траектории, задаваемой с клавиатуры. Описанная здесь процедура примеча- тельна тем, что работает очень быстро. Возьмем для примера вторую часть из всем известной словацкой демки "ECHOLOGY", векторные объекты там также вращаются в реальном времени. Понажимав цифровые кла- виши, можно убрать линии и оставить одни вектора. Проследите скорость работы эф- фектов. На моем Pentagon'е в одно преры- вание укладываются фигуры с количеством векторов, не превышающим 28, фигуры с ко- личеством векторов 29 и больше уже тормо- зят. Данный же способ позволяет вращать по всем трем осям + приближать/удалять/ рас- тягивать и т.д. аж 175 векторов и больше! Основная суть данного метода состоит в том, что вращается не каждая точка в от- дельности, а сразу целая плоскость. Этот метод может пригодиться не только для создания крутых векторных эффектов, но и для написания новых игрушек. А как rulez'но будут смотреться старые вектор- ные игры, переделанные с учетом последне- го слова техники?! Представьте, например, ELITE, работающую в 2-3 раза быстрее или плавнее! Так что дерзайте, GOOD LUCK! Коментарии (C) BY VIATOR/AVALON/RUSH/ASM _________________________________________ Из журнала ZX Power #4, Харьков, 1999 Об ошибках статьи "3D графика" в ZX-Power#3 (С) 1999, Пелепейченко Александр __________________________________________ От редакции: В прошлом номере журнала была опублико- вана статья о работе с трехмерными объек- тами, подготовленная нашими друзьями из Avalon - (C) Ruff&Viator. И вот через не- которое время один из читателей сообщил нам о своих замечаниях к вышеупомянутой статье. Публикуем их здесь, рассчитывая на комментарии со стороны Ruff'а... * * * Загрузил я как-то ZX-Power#3 и стал его читать. Собственно, ничего удивительного в этом нет. Так вот, читаю я его, читаю и вдруг натыкаюсь на эту статью. Поскольку я являюсь поклонником 3D графики и разных там эффектов, решаю разобраться в этом способе. К тому же многообещающее предис- ловие разжигает интерес к нижеуказанному способу. Впоследствии я, конечно, понял всю ру- лезность этого способа, но надо сказать, что поначалу я не только не мог врубиться в прилагаемый код, но и ставил под большое сомнение теоретическую часть (в нее я тоже не сразу врубился). Сразу скажу, что спо- соб очень похож на предложенный в SPECTRUM EXPERT#2 "метод средней точки". Внимательно прочитав с десяток раз эту статью, и так и не поняв многого, я решил набить это и проверить его работоспособ- ность. И тут началось самое интересное. Во-первых, в конце процедуры К64 я увидел непонятно откуда взявшийся кусок кода (а точнее даже 2 куска: первый - метка LOOP4 и до конца, второй - то что над этой мет- кой до команды RET). Изучив эти куски я понял, что это подсчет тех же осей, но с учетом ненулевого значения 3D-центра. Во- обще, он (этот кусок) ТАМ не очень нужен (разве что для наглядности). Но... (читай вторую часть статьи). Немного присмотревшись к тексту, я ре- шил, что, вероятнее всего, какая-то часть текста была утеряна. На эту мысль меня на- толкнуло то, что упоминаются рисунки с но- мером 2 и 3, а их там в помине нету. Прим. Ред.: Напечатали все, что было... Оказалось, что координаты точек попута- ны бог знает каким образом, и их печать идет вообще неправильно. Тогда мне стало ясно - ошибки в статье. ALASM был загружен, делать было нечего, и я принялся за отладку программы. Теоре- тическую часть я принял на веру, не прове- ряя ее, а вот код, как оказалось, немного не соответствовал теории. Ошибки щедро одарили подпрограмму AX. Непонятно зачем в самом начале этой проце- дуры было нечто вроде: LD A,E SUB 32 LD E,A ... ; и так далее с регистрами ; L, C , L' Прим. Ред.: Мы их туда не добавляли. :)) Далее, причиной отфонарной печати точек и непонятно какой последовательности хра- нения информации про эти точки было попу- танное местами засылание на стек коорди- нат. Я, конечно, понимаю, что несколько ошибок могут компенсировать друг друга и не считаться ошибками (что могло иметь место в исходниках автора), но в приведен- ном листинге такого не происходило. Веро- ятно, эти ошибки возникли потому, что код в статью писался по памяти. Весь код, ко- торый вы видите здесь, вытянут из уже ра- ботающих исходников, поэтому ошибки подоб- ного рода маловероятны. Я приведу сдесь ВСЮ процедуру AX, так как 80% ее части в POWER#3 являются неверной. На входе в AX в регистрах задаются координаты ключевых то- чек, уже спроектированных в экран. Причем в старшей половине регистра сидит X-коор- дината. TABAX EQU 250 ;вход: ;DE = центр 3D AX LD HX,D ;BC = конец оси z LD LX,E ;HL = конец оси x EXX ;HL'= конец оси y LD A,L ; в POWER#3 PUSH AF ; именно эти PUSH'ы EXX ; оказались LD A,C ; немного PUSH AF ; попутаны LD A,L ; PUSH AF ; LD A,H ; PUSH AF ; EXX ; LD A,H ; PUSH AF ; EXX ; LD A,TABAX LD (VAR),A LD A,B ; в POWER#3 стоит LD A,H LD B,HX CALL K64 ; (*) POP AF LD B,HX CALL K64 POP AF LD B,HX CALL K64 POP AF LD B,LX CALL K64 ; (*) POP AF LD B,LX CALL K64 POP AF LD B,LX CALL K64 RET VAR DEFB 0 Пояснение (*) см. во второй части. Не миновали опечатки и процедуру К64. В том месте, где стоят такие команды (ищите их после метки LOOP3) : LOOP3 ...... DUP 64 SBC HL,DE LD A,H EXX ...... Необходима замена на: DUP 64 LD A,H SBC HL,DE EXX ...... То есть команда LD A,H должна стоять ПЕРЕД SBC HL,DE, иначе таблица оказывается сдвинутой на 1 байт вниз. Больше ошибок и неточностей я не нашел. И несмотря на все вышесказанное, я хочу заметить, что, вероятно, если бы не эти ошибки, я бы так и не разобрался бы с принципом работы этого метода. Конечно, главное - идея, а ошибаться склонен каж- дый. Ну, с ошибками покончено, теперь мои изыскания. часть 2 Метод очень производительный, хотя мож- но сделать его еще быстрее, что несколько раздует К64. Этого можно добиться, если к одной из x-координат при расчете осей при- бавить HX, и то же самое проделать с одной из y-координат, после чего убрать эти при- бавления из процедуры рисования точек. Вы- игрыш будет точно 12 тактов на точке. В этом случае можно приспособить тот кусок кода в К64, о котором я говорил выше.(Мо- жет он и был приспособлен для этого, но коментарий к нему затерялся в редакции). Прим. Ред.: Ну, это уже "наезд"! :)) У нас, в редакции, "затеряться" может только вся статья целиком, но тогда мы ее не на- печатаем... ;)) Для этого нужно именовать его какой-ли- бо меткой, например К64_1. Теперь необходимо CALL K64, помеченные (*) (смотри выше), заменить на CALL K64_1 и убрать команды ADD A,HX и ADD A,LX из процедуры вывода точек. Была идея сделать 4D (5D,6D...) ренде- ринг, однако не было времени, да и без хо- рошей вращалки/леталки делать это нет смысла. А вообще я подумал: "Зачем пользоваться альтернативным регистром hl' в К64, если есть еще неиспользующийся BC??? Тогда (за счет аннулирования EXX'ов) на просчете од- ной оси по обеим координатам удастся сэко- номить еще 8*64*2=1024 тактов + около 256 байт, где 8-сумма EXX'ов в тактах, 64 - у нас там DUP 64, 2 - у одной оси две коор- динаты (x и y). А таких осей 3 штуки..." Подумал я так и накодил более быструю про- цедуру, которая экономит около 3072 тактов на каждом просчете осей. K64 CP B ; эта процедура JP C,LOOP1 ; целиком и полностью SUB B ; вытянута из рабочих LD L,A ; исходников, так что LD H,0 ; ошибки вряд ли ADD HL,HL ; найдутся ADD HL,HL EX DE,HL LD HL,0 LOOP2 LD A,(VAR) LD B,A INC A LD (VAR),A LD C,0 DUP 64 LD A,H ADD HL,DE LD (BC),A INC C EDUP RET LOOP1 LD C,A LD A,B SUB C LD L,A LD H,0 ADD HL,HL ADD HL,HL EX DE,HL LD HL,0 LOOP3 LD A,(VAR) LD B,A INC A LD (VAR),A LD C,0 DUP 64 LD A,H SBC HL,DE LD (BC),A INC C EDUP RET K64_1 CP B JR C,LOOP4 SUB B LD L,A LD H,0 ADD HL,HL ADD HL,HL EX DE,HL LD H,B LD L,0 JP LOOP2 LOOP4 LD C,A LD A,B SUB C LD L,A LD H,0 ADD HL,HL ADD HL,HL EX DE,HL ADD A,C LD H,A LD L,0 JP LOOP3 И еще одно. Если мы уж и стираем с эк- рана избражение для вывода нового, то де- лать это желательно как можно быстрее, что я и предлагаю сделать заменив POP HL:LD (HL),0:POP HL..... таким вариантом: XOR A POP HL:LD (HL),A:POP HL:LD (HL),A... А вообще я так и не смог нарисовать нормальный 3D обьект, состоящий полностью из точек и решил (но еще не сделал) подме- шать draw'ы. Естественно, предложенная стиралка оказалась неприемлимой. Правда, на точках проще отсекать неви- димые и заходящие за экран части, но нари- совать обьект....(у меня после попытки на- рисовать появилась идея сделать мини 3D STUDIO). Проблему перспективы проще всего решить перспективно спроектиравав 3D систему ко- ординат. На этом все. __________________________________________