§ DosBox

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

§ Алгоритм

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

§ Код на FASM

; ------------------------------------------------------------------------------
; Основная программа
; ------------------------------------------------------------------------------

        org     100h
        call    scr13
        call    clear
cycle:  mov     [box.cl], byte 10
        call    drawcube                ; Рисование
        call    copyscr                 ; Вывод на экран
        mov     [box.cl], byte 0
        call    drawcube                ; Удаление куба
        add     word [box.rx], 1
        add     word [box.ry], 2
        add     word [box.rz], 1
        jmp     cycle

; ------------------------------------------------------------------------------
; Нарисовать куб
; ------------------------------------------------------------------------------

drawcube:

        ; Указатель на индексы граней
        mov     si, a_face

        ; Проекция 4 точек
.next:  mov     cx, 4
        mov     di, point
@@:     lodsb
        call    proj
        mov     [di+0], ax      ; px
        mov     [di+2], bx      ; py
        add     di, 4
        loop    @b

        ; Проверка фронтальной стороны
        mov     di, point.a
        mov     ax, [di+4+2]
        sub     ax, [di+0+2]    ; ABy = p(1).y - p(0).y
        mov     bx, [di+8+0]
        sub     bx, [di+0+0]    ; ACx = p(2).x - p(0).x
        imul    bx, ax
        mov     ax, [di+4+0]
        sub     ax, [di+0+0]    ; ABx = p(1).x - p(0).x
        mov     cx, [di+8+2]
        sub     cx, [di+0+2]    ; ACy = p(2).y - p(0).y
        imul    ax, cx
        cmp     bx, ax          ; ABx * ACy - ABy * ACx
        jle     .skip

        ; Нарисовать грань куба
        push    si
        mov     si, point.a
        mov     di, point.b
.rpt:   mov     ax, [si+0]
        mov     bx, [si+2]
        mov     cx, [di+0]
        mov     dx, [di+2]
        mov     bp, [box.cl]
        call    line
        add     si, 4
        add     di, 4
        cmp     si, point.d + 4 ; Выход за пределы
        je      .end
        cmp     si, point.d     ; Если D-то следующая точка A
        jne     .rpt
        mov     di, point.a
        jmp     .rpt
.end:   pop     si

        ; Количество граней, 6
.skip:  cmp     si, a_face + (4*6)
        jne     .next
        ret

; ------------------------------------------------------------------------------
; Рассчитать проекцию PX, PY => AX, BX
; ------------------------------------------------------------------------------

proj:   push    cx
        movzx   ebx, al
        movsx   ecx, byte [a_vtx + 3*ebx + 2]    ; Z
        movsx   eax, byte [a_vtx + 3*ebx + 1]    ; Y
        movsx   ebx, byte [a_vtx + 3*ebx + 0]    ; X

        ; Умножить на 256 всё
        shl     ax, 8 ; y
        shl     bx, 8 ; x
        shl     cx, 8 ; z

        ; Повернуть куб по тем осям
        xchg    eax, ecx            ; Вокруг оси Y (Z,X)
        mov     dx, [box.ry]
        call    rotate
        xchg    eax, ecx            ; Вокруг оси Z (Y,X)
        mov     dx, [box.rz]
        call    rotate
        xchg    ebx, ecx            ; Вокруг оси X (Y,Z)
        mov     dx, [box.rx]
        call    rotate
        xchg    ebx, ecx

        ; Добавить итоговое смещение камеры
        add     ebx, [cam.x]
        add     eax, [cam.y]
        add     ecx, [cam.z]
        imul    eax, 200
        imul    ebx, 200

        ; {ax,bx} = {160 + 200*x/z, 100 - 200*y/z}
        cdq
        idiv    ecx
        xchg    eax, ebx
        cdq
        idiv    ecx
        add     ax, 160
        sub     bx, 100
        neg     bx
        pop     cx
        ret

; ------------------------------------------------------------------------------
; Вращение AX, BX при помощи FPU на угол DX
; ------------------------------------------------------------------------------

rotate: mov     [.x], ax
        mov     [.y], bx
        mov     [.r], dx

        ; Вычислить sina, cosa
        fild    word [.r]
        fld     dword [.f360]
        fdivp   st1,st0         ; r/360
        fsincos

        ; eax = x*cosa - y*sina
        fild    word [.y]
        fild    word [.x]
        fmul    st0,st2         ; x*cosa
        fxch    st1
        fmul    st0,st3         ; y*sina
        fsubp   st1,st0         ; R=x*cosa - y*sina
        fistp   dword [.t]
        mov     eax, [.t]

        ; ebx = y*cosa + x*sina
        fild    word [.x]
        fild    word [.y]
        fmulp   st2,st0         ; y*cosa
        fmulp   st2,st0         ; x*sina
        faddp   st1,st0         ; R=y*cosa + x*sina
        fistp   dword [.t]
        mov     ebx, [.t]

        ret

; Локальные переменные
.f360:  dd      100.0           ; 180/pi(57.295) * speed
.x:     dw      0
.y:     dw      0
.t:     dd      0
.r:     dw      0

; ------------------------------------------------------------------------------
; Фреймбуфер
; ------------------------------------------------------------------------------
; Видеорежим 320x200
scr13:  mov     ax, 0013h
        int     10h
        ret

; ES=CS+1000h
loadvb: mov     ax, cs
        add     ax, 1000h
        mov     es, ax
        ret

; Очистить буфер
clear:  call    loadvb
        xor     di, di
        xor     eax, eax
        mov     cx, 16000
        rep     stosd
        ret

; Копировать из буфера на экран
copyscr:

        push    ds es
        xor     si, si
        xor     di, di
        call    loadvb
        push    es
        mov     ax, $A000
        mov     es, ax
        pop     ds
        mov     cx, 16000
        rep     movsd
        pop     es ds
        ret

; ------------------------------------------------------------------------------
; Рисование линии
; x1=ax, y1=bx -> x2=cx, y2=dx; bp-цвет
; ------------------------------------------------------------------------------

line:   push    si di
        mov     si, 1       ; int (si) signx  = x1 < x2 ? 1 : -1;
        mov     di, 1       ; int (di) signy  = y1 < y2 ? 1 : -1;
        mov     [.x2], cx
        mov     [.y2], dx
        sub     cx, ax      ; int (cx) deltax = |x2 - x1|
        jge     @f
        neg     cx
        neg     si
@@:     sub     dx, bx      ; int (dx) deltay = |y2 - y1|
        jge     @f
        neg     dx
        neg     di
@@:     mov     [.sx], si   ; signx
        mov     [.sy], di   ; signy
        mov     [.dx], cx   ; deltax = |x2 - x1|
        mov     [.dy], dx   ; deltay = |y2 - y1|
        mov     si, cx      ; error = deltax - deltay
        sub     si, dx
        mov     dx, bp
.ps:    cmp     ax, 320     ; ax=[0..319], bx=[0..119]
        jnb     @f
        cmp     bx, 200
        jnb     @f
        imul    di, bx, 320 ; PSET (ax, bx)
        add     di, ax
        mov     [es: di], dl
@@:     cmp     ax, [.x2]   ; while ((x1 != x2) || (y1 != y2))
        jne     @f
        cmp     bx, [.y2]
        jne     @f
        pop     di si
        ret
@@:     mov     cx, si      ; error2 = 2*error
        add     cx, cx
        cmp     cx, [.dx]   ; if (error2 - deltax < 0):
        jg      @f
        add     si, [.dx]   ; error += deltax; y1 += signy
        add     bx, [.sy]
@@:     add     cx, [.dy]   ; if (error2 + deltay > 0):
        jle     .ps
        sub     si, [.dy]   ; error -= deltay; x1 += signx
        add     ax, [.sx]
        jmp     .ps
.sx:    dw      0
.sy:    dw      0
.dx:    dw      0
.dy:    dw      0
.x2:    dw      0
.y2:    dw      0
; ------------------------------------------------------------------------------

;   4---5
;  /|  /|
; 0---1 |
; | 7_|_6
; |/  |/
; 3---2

; Цвет и вращение куба
box:
.cl:    db      14
.rx:    dw      0
.ry:    dw      0
.rz:    dw      0

; Вершины (8)
a_vtx:  db      -1, 1, 1    ; 0
        db       1, 1, 1    ; 1
        db       1,-1, 1    ; 2
        db      -1,-1, 1    ; 3
        db      -1, 1,-1    ; 4
        db       1, 1,-1    ; 5
        db       1,-1,-1    ; 6
        db      -1,-1,-1    ; 7

; Грани (6)
a_face: db      0,1,2,3     ; 0
        db      1,5,6,2     ; 1
        db      5,4,7,6     ; 2
        db      4,0,3,7     ; 3
        db      6,7,3,2     ; 4
        db      4,5,1,0     ; 5
; ------------------------------------------------------------------------------
cam:
.x:     dd      0
.y:     dd      0
.z:     dd      1200
; ------------------------------------------------------------------------------
point:
.a:     dw      ?, ?
.b:     dw      ?, ?
.c:     dw      ?, ?
.d:     dw      ?, ?