§ DosBox

Красиво этот кубик работает только в DosBOX, где можно отрегулировать скорость вращения путем уменьшения или увеличения циклов.

§ Алгоритм

Фрейм
  • Рисование треугольника
  • Копирование фрейма в видеопамять
  • Удаление треугольника
  • Добавление вращений по осям
Куб
  • Считывание и рисование 6 граней
Грань
  • Получение номера 4 точек
  • Получение (x,y,z) точек и выполнение вращения
  • Проекция (x,y,z) -> (px,py)
  • Проверка на фронтальную сторону грани
  • Рисование 4 линии

§ Код на FASM

1; ------------------------------------------------------------------------------
2; Основная программа
3; ------------------------------------------------------------------------------
4
5        org     100h
6        call    scr13
7        call    clear
8cycle:  mov     [box.cl], byte 10
9        call    drawcube                ; Рисование
10        call    copyscr                 ; Вывод на экран
11        mov     [box.cl], byte 0
12        call    drawcube                ; Удаление куба
13        add     word [box.rx], 1
14        add     word [box.ry], 2
15        add     word [box.rz], 1
16        jmp     cycle
17
18; ------------------------------------------------------------------------------
19; Нарисовать куб
20; ------------------------------------------------------------------------------
21
22drawcube:
23
24        ; Указатель на индексы граней
25        mov     si, a_face
26
27        ; Проекция 4 точек
28.next:  mov     cx, 4
29        mov     di, point
30@@:     lodsb
31        call    proj
32        mov     [di+0], ax      ; px
33        mov     [di+2], bx      ; py
34        add     di, 4
35        loop    @b
36
37        ; Проверка фронтальной стороны
38        mov     di, point.a
39        mov     ax, [di+4+2]
40        sub     ax, [di+0+2]    ; ABy = p(1).y - p(0).y
41        mov     bx, [di+8+0]
42        sub     bx, [di+0+0]    ; ACx = p(2).x - p(0).x
43        imul    bx, ax
44        mov     ax, [di+4+0]
45        sub     ax, [di+0+0]    ; ABx = p(1).x - p(0).x
46        mov     cx, [di+8+2]
47        sub     cx, [di+0+2]    ; ACy = p(2).y - p(0).y
48        imul    ax, cx
49        cmp     bx, ax          ; ABx * ACy - ABy * ACx
50        jle     .skip
51
52        ; Нарисовать грань куба
53        push    si
54        mov     si, point.a
55        mov     di, point.b
56.rpt:   mov     ax, [si+0]
57        mov     bx, [si+2]
58        mov     cx, [di+0]
59        mov     dx, [di+2]
60        mov     bp, [box.cl]
61        call    line
62        add     si, 4
63        add     di, 4
64        cmp     si, point.d + 4 ; Выход за пределы
65        je      .end
66        cmp     si, point.d     ; Если D-то следующая точка A
67        jne     .rpt
68        mov     di, point.a
69        jmp     .rpt
70.end:   pop     si
71
72        ; Количество граней, 6
73.skip:  cmp     si, a_face + (4*6)
74        jne     .next
75        ret
76
77; ------------------------------------------------------------------------------
78; Рассчитать проекцию PX, PY => AX, BX
79; ------------------------------------------------------------------------------
80
81proj:   push    cx
82        movzx   ebx, al
83        movsx   ecx, byte [a_vtx + 3*ebx + 2]    ; Z
84        movsx   eax, byte [a_vtx + 3*ebx + 1]    ; Y
85        movsx   ebx, byte [a_vtx + 3*ebx + 0]    ; X
86
87        ; Умножить на 256 всё
88        shl     ax, 8 ; y
89        shl     bx, 8 ; x
90        shl     cx, 8 ; z
91
92        ; Повернуть куб по тем осям
93        xchg    eax, ecx            ; Вокруг оси Y (Z,X)
94        mov     dx, [box.ry]
95        call    rotate
96        xchg    eax, ecx            ; Вокруг оси Z (Y,X)
97        mov     dx, [box.rz]
98        call    rotate
99        xchg    ebx, ecx            ; Вокруг оси X (Y,Z)
100        mov     dx, [box.rx]
101        call    rotate
102        xchg    ebx, ecx
103
104        ; Добавить итоговое смещение камеры
105        add     ebx, [cam.x]
106        add     eax, [cam.y]
107        add     ecx, [cam.z]
108        imul    eax, 200
109        imul    ebx, 200
110
111        ; {ax,bx} = {160 + 200*x/z, 100 - 200*y/z}
112        cdq
113        idiv    ecx
114        xchg    eax, ebx
115        cdq
116        idiv    ecx
117        add     ax, 160
118        sub     bx, 100
119        neg     bx
120        pop     cx
121        ret
122
123; ------------------------------------------------------------------------------
124; Вращение AX, BX при помощи FPU на угол DX
125; ------------------------------------------------------------------------------
126
127rotate: mov     [.x], ax
128        mov     [.y], bx
129        mov     [.r], dx
130
131        ; Вычислить sina, cosa
132        fild    word [.r]
133        fld     dword [.f360]
134        fdivp   st1,st0         ; r/360
135        fsincos
136
137        ; eax = x*cosa - y*sina
138        fild    word [.y]
139        fild    word [.x]
140        fmul    st0,st2         ; x*cosa
141        fxch    st1
142        fmul    st0,st3         ; y*sina
143        fsubp   st1,st0         ; R=x*cosa - y*sina
144        fistp   dword [.t]
145        mov     eax, [.t]
146
147        ; ebx = y*cosa + x*sina
148        fild    word [.x]
149        fild    word [.y]
150        fmulp   st2,st0         ; y*cosa
151        fmulp   st2,st0         ; x*sina
152        faddp   st1,st0         ; R=y*cosa + x*sina
153        fistp   dword [.t]
154        mov     ebx, [.t]
155
156        ret
157
158; Локальные переменные
159.f360:  dd      100.0           ; 180/pi(57.295) * speed
160.x:     dw      0
161.y:     dw      0
162.t:     dd      0
163.r:     dw      0
164
165; ------------------------------------------------------------------------------
166; Фреймбуфер
167; ------------------------------------------------------------------------------
168; Видеорежим 320x200
169scr13:  mov     ax, 0013h
170        int     10h
171        ret
172
173; ES=CS+1000h
174loadvb: mov     ax, cs
175        add     ax, 1000h
176        mov     es, ax
177        ret
178
179; Очистить буфер
180clear:  call    loadvb
181        xor     di, di
182        xor     eax, eax
183        mov     cx, 16000
184        rep     stosd
185        ret
186
187; Копировать из буфера на экран
188copyscr:
189
190        push    ds es
191        xor     si, si
192        xor     di, di
193        call    loadvb
194        push    es
195        mov     ax, $A000
196        mov     es, ax
197        pop     ds
198        mov     cx, 16000
199        rep     movsd
200        pop     es ds
201        ret
202
203; ------------------------------------------------------------------------------
204; Рисование линии
205; x1=ax, y1=bx -> x2=cx, y2=dx; bp-цвет
206; ------------------------------------------------------------------------------
207
208line:   push    si di
209        mov     si, 1       ; int (si) signx  = x1 < x2 ? 1 : -1;
210        mov     di, 1       ; int (di) signy  = y1 < y2 ? 1 : -1;
211        mov     [.x2], cx
212        mov     [.y2], dx
213        sub     cx, ax      ; int (cx) deltax = |x2 - x1|
214        jge     @f
215        neg     cx
216        neg     si
217@@:     sub     dx, bx      ; int (dx) deltay = |y2 - y1|
218        jge     @f
219        neg     dx
220        neg     di
221@@:     mov     [.sx], si   ; signx
222        mov     [.sy], di   ; signy
223        mov     [.dx], cx   ; deltax = |x2 - x1|
224        mov     [.dy], dx   ; deltay = |y2 - y1|
225        mov     si, cx      ; error = deltax - deltay
226        sub     si, dx
227        mov     dx, bp
228.ps:    cmp     ax, 320     ; ax=[0..319], bx=[0..119]
229        jnb     @f
230        cmp     bx, 200
231        jnb     @f
232        imul    di, bx, 320 ; PSET (ax, bx)
233        add     di, ax
234        mov     [es: di], dl
235@@:     cmp     ax, [.x2]   ; while ((x1 != x2) || (y1 != y2))
236        jne     @f
237        cmp     bx, [.y2]
238        jne     @f
239        pop     di si
240        ret
241@@:     mov     cx, si      ; error2 = 2*error
242        add     cx, cx
243        cmp     cx, [.dx]   ; if (error2 - deltax < 0):
244        jg      @f
245        add     si, [.dx]   ; error += deltax; y1 += signy
246        add     bx, [.sy]
247@@:     add     cx, [.dy]   ; if (error2 + deltay > 0):
248        jle     .ps
249        sub     si, [.dy]   ; error -= deltay; x1 += signx
250        add     ax, [.sx]
251        jmp     .ps
252.sx:    dw      0
253.sy:    dw      0
254.dx:    dw      0
255.dy:    dw      0
256.x2:    dw      0
257.y2:    dw      0
258; ------------------------------------------------------------------------------
259
260;   4---5
261;  /|  /|
262; 0---1 |
263; | 7_|_6
264; |/  |/
265; 3---2
266
267; Цвет и вращение куба
268box:
269.cl:    db      14
270.rx:    dw      0
271.ry:    dw      0
272.rz:    dw      0
273
274; Вершины (8)
275a_vtx:  db      -1, 1, 1    ; 0
276        db       1, 1, 1    ; 1
277        db       1,-1, 1    ; 2
278        db      -1,-1, 1    ; 3
279        db      -1, 1,-1    ; 4
280        db       1, 1,-1    ; 5
281        db       1,-1,-1    ; 6
282        db      -1,-1,-1    ; 7
283
284; Грани (6)
285a_face: db      0,1,2,3     ; 0
286        db      1,5,6,2     ; 1
287        db      5,4,7,6     ; 2
288        db      4,0,3,7     ; 3
289        db      6,7,3,2     ; 4
290        db      4,5,1,0     ; 5
291; ------------------------------------------------------------------------------
292cam:
293.x:     dd      0
294.y:     dd      0
295.z:     dd      1200
296; ------------------------------------------------------------------------------
297point:
298.a:     dw      ?, ?
299.b:     dw      ?, ?
300.c:     dw      ?, ?
301.d:     dw      ?, ?